wavexis 2.2.4__tar.gz → 2.2.6__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 (213) hide show
  1. {wavexis-2.2.4 → wavexis-2.2.6}/CHANGELOG.md +21 -0
  2. {wavexis-2.2.4 → wavexis-2.2.6}/PKG-INFO +1 -1
  3. {wavexis-2.2.4 → wavexis-2.2.6}/pyproject.toml +1 -1
  4. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/__init__.py +1 -1
  5. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_session.py +52 -6
  6. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_shared.py +15 -5
  7. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/exceptions.py +28 -3
  8. {wavexis-2.2.4 → wavexis-2.2.6}/.dockerignore +0 -0
  9. {wavexis-2.2.4 → wavexis-2.2.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  10. {wavexis-2.2.4 → wavexis-2.2.6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  11. {wavexis-2.2.4 → wavexis-2.2.6}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  12. {wavexis-2.2.4 → wavexis-2.2.6}/.github/workflows/ci.yml +0 -0
  13. {wavexis-2.2.4 → wavexis-2.2.6}/.github/workflows/docs.yml +0 -0
  14. {wavexis-2.2.4 → wavexis-2.2.6}/.github/workflows/release.yml +0 -0
  15. {wavexis-2.2.4 → wavexis-2.2.6}/.gitignore +0 -0
  16. {wavexis-2.2.4 → wavexis-2.2.6}/CODE_OF_CONDUCT.md +0 -0
  17. {wavexis-2.2.4 → wavexis-2.2.6}/CONTRIBUTING.md +0 -0
  18. {wavexis-2.2.4 → wavexis-2.2.6}/Dockerfile +0 -0
  19. {wavexis-2.2.4 → wavexis-2.2.6}/LICENSE +0 -0
  20. {wavexis-2.2.4 → wavexis-2.2.6}/README.md +0 -0
  21. {wavexis-2.2.4 → wavexis-2.2.6}/SECURITY.md +0 -0
  22. {wavexis-2.2.4 → wavexis-2.2.6}/docs/api/actions.md +0 -0
  23. {wavexis-2.2.4 → wavexis-2.2.6}/docs/api/backends.md +0 -0
  24. {wavexis-2.2.4 → wavexis-2.2.6}/docs/api/cli.md +0 -0
  25. {wavexis-2.2.4 → wavexis-2.2.6}/docs/api/config.md +0 -0
  26. {wavexis-2.2.4 → wavexis-2.2.6}/docs/api/exceptions.md +0 -0
  27. {wavexis-2.2.4 → wavexis-2.2.6}/docs/api/multi.md +0 -0
  28. {wavexis-2.2.4 → wavexis-2.2.6}/docs/api/plugins.md +0 -0
  29. {wavexis-2.2.4 → wavexis-2.2.6}/docs/api/serve.md +0 -0
  30. {wavexis-2.2.4 → wavexis-2.2.6}/docs/cookbook/auth.md +0 -0
  31. {wavexis-2.2.4 → wavexis-2.2.6}/docs/cookbook/ci-cd.md +0 -0
  32. {wavexis-2.2.4 → wavexis-2.2.6}/docs/cookbook/css.md +0 -0
  33. {wavexis-2.2.4 → wavexis-2.2.6}/docs/cookbook/debug.md +0 -0
  34. {wavexis-2.2.4 → wavexis-2.2.6}/docs/cookbook/record-replay.md +0 -0
  35. {wavexis-2.2.4 → wavexis-2.2.6}/docs/cookbook/scraping.md +0 -0
  36. {wavexis-2.2.4 → wavexis-2.2.6}/docs/cookbook/screenshots.md +0 -0
  37. {wavexis-2.2.4 → wavexis-2.2.6}/docs/cookbook/serve-mode.md +0 -0
  38. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/assert.md +0 -0
  39. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/backends.md +0 -0
  40. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/commands.md +0 -0
  41. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/init.md +0 -0
  42. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/installation.md +0 -0
  43. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/multi.md +0 -0
  44. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/perf.md +0 -0
  45. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/plugins.md +0 -0
  46. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/quickstart.md +0 -0
  47. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/raw.md +0 -0
  48. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/repl.md +0 -0
  49. {wavexis-2.2.4 → wavexis-2.2.6}/docs/guide/troubleshooting.md +0 -0
  50. {wavexis-2.2.4 → wavexis-2.2.6}/docs/index.md +0 -0
  51. {wavexis-2.2.4 → wavexis-2.2.6}/mkdocs.yml +0 -0
  52. {wavexis-2.2.4 → wavexis-2.2.6}/stubs/bidiwave/__init__.pyi +0 -0
  53. {wavexis-2.2.4 → wavexis-2.2.6}/stubs/cdpwave/__init__.pyi +0 -0
  54. {wavexis-2.2.4 → wavexis-2.2.6}/tests/__init__.py +0 -0
  55. {wavexis-2.2.4 → wavexis-2.2.6}/tests/conftest.py +0 -0
  56. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/__init__.py +0 -0
  57. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_a11y.py +0 -0
  58. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_animation.py +0 -0
  59. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_backend_selection.py +0 -0
  60. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_browser.py +0 -0
  61. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_console.py +0 -0
  62. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_css.py +0 -0
  63. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_debug.py +0 -0
  64. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_dialog.py +0 -0
  65. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_dom.py +0 -0
  66. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_dom_snapshot.py +0 -0
  67. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_emulation.py +0 -0
  68. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_emulation_advanced.py +0 -0
  69. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_eval.py +0 -0
  70. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_har.py +0 -0
  71. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_input.py +0 -0
  72. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_media.py +0 -0
  73. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_multi.py +0 -0
  74. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_navigate.py +0 -0
  75. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_network.py +0 -0
  76. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_network_advanced.py +0 -0
  77. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_overlay.py +0 -0
  78. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_pdf.py +0 -0
  79. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_perf.py +0 -0
  80. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_permissions.py +0 -0
  81. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_raw.py +0 -0
  82. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_record.py +0 -0
  83. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_scrape.py +0 -0
  84. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_screenshot.py +0 -0
  85. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_security.py +0 -0
  86. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_serve.py +0 -0
  87. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_service_worker.py +0 -0
  88. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_storage.py +0 -0
  89. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_tabs.py +0 -0
  90. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_webaudio.py +0 -0
  91. {wavexis-2.2.4 → wavexis-2.2.6}/tests/integration/test_webauthn.py +0 -0
  92. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/__init__.py +0 -0
  93. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_abstract_backend_phase5.py +0 -0
  94. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_actions.py +0 -0
  95. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_actions_phase5.py +0 -0
  96. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_animation_action.py +0 -0
  97. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_assert_multi.py +0 -0
  98. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_auth.py +0 -0
  99. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_backend_manager.py +0 -0
  100. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_bidi_backend.py +0 -0
  101. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_bidi_phase5.py +0 -0
  102. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_bluetooth_action.py +0 -0
  103. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_cast_action.py +0 -0
  104. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_cleanup.py +0 -0
  105. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_cli_phase5.py +0 -0
  106. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_config.py +0 -0
  107. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_config_phase5.py +0 -0
  108. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_cookies_headers_actions.py +0 -0
  109. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_css_action.py +0 -0
  110. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_debug_action.py +0 -0
  111. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_dom_snapshot_action.py +0 -0
  112. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_download_screencast.py +0 -0
  113. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_emulation_action.py +0 -0
  114. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_error_handling.py +0 -0
  115. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_exceptions.py +0 -0
  116. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_format_record.py +0 -0
  117. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_global_options.py +0 -0
  118. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_init.py +0 -0
  119. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_input_action.py +0 -0
  120. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_interactions_crawl.py +0 -0
  121. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_manager.py +0 -0
  122. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_media_action.py +0 -0
  123. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_multi.py +0 -0
  124. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_multi_improvements.py +0 -0
  125. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_new_features.py +0 -0
  126. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_output.py +0 -0
  127. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_overlay_action.py +0 -0
  128. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_perf_action.py +0 -0
  129. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_perf_console.py +0 -0
  130. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_plugins.py +0 -0
  131. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_raw.py +0 -0
  132. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_record.py +0 -0
  133. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_repl.py +0 -0
  134. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_serve.py +0 -0
  135. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_service_worker_action.py +0 -0
  136. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_storage_action.py +0 -0
  137. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_webaudio_action.py +0 -0
  138. {wavexis-2.2.4 → wavexis-2.2.6}/tests/unit/test_webauthn_action.py +0 -0
  139. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/__main__.py +0 -0
  140. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/__init__.py +0 -0
  141. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/accessibility.py +0 -0
  142. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/animation.py +0 -0
  143. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/base.py +0 -0
  144. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/bluetooth.py +0 -0
  145. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/browser.py +0 -0
  146. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/cast.py +0 -0
  147. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/console.py +0 -0
  148. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/cookies.py +0 -0
  149. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/crawl.py +0 -0
  150. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/css.py +0 -0
  151. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/debug.py +0 -0
  152. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/dialog.py +0 -0
  153. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/dom.py +0 -0
  154. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/dom_snapshot.py +0 -0
  155. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/download.py +0 -0
  156. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/emulation.py +0 -0
  157. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/eval.py +0 -0
  158. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/extract.py +0 -0
  159. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/form.py +0 -0
  160. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/har.py +0 -0
  161. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/headers.py +0 -0
  162. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/input.py +0 -0
  163. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/lighthouse.py +0 -0
  164. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/media.py +0 -0
  165. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/multi.py +0 -0
  166. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/navigate.py +0 -0
  167. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/network.py +0 -0
  168. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/overlay.py +0 -0
  169. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/pdf.py +0 -0
  170. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/performance.py +0 -0
  171. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/permissions.py +0 -0
  172. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/record.py +0 -0
  173. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/scrape.py +0 -0
  174. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/screencast.py +0 -0
  175. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/screenshot.py +0 -0
  176. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/security.py +0 -0
  177. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/service_worker.py +0 -0
  178. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/session.py +0 -0
  179. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/storage.py +0 -0
  180. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/tabs.py +0 -0
  181. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/wait.py +0 -0
  182. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/webaudio.py +0 -0
  183. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/webauthn.py +0 -0
  184. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/actions/websocket.py +0 -0
  185. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/auth.py +0 -0
  186. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/backend/__init__.py +0 -0
  187. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/backend/base.py +0 -0
  188. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/backend/bidi.py +0 -0
  189. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/backend/cdp.py +0 -0
  190. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/backend/manager.py +0 -0
  191. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cleanup.py +0 -0
  192. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/__init__.py +0 -0
  193. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_advanced.py +0 -0
  194. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_capture.py +0 -0
  195. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_config.py +0 -0
  196. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_debug.py +0 -0
  197. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_emulation.py +0 -0
  198. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_experimental.py +0 -0
  199. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_input.py +0 -0
  200. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_navigation.py +0 -0
  201. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_network.py +0 -0
  202. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_perf.py +0 -0
  203. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_serve.py +0 -0
  204. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/_workflow.py +0 -0
  205. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/cli/app.py +0 -0
  206. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/config.py +0 -0
  207. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/init.py +0 -0
  208. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/multi.py +0 -0
  209. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/output.py +0 -0
  210. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/plugins.py +0 -0
  211. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/record.py +0 -0
  212. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/repl.py +0 -0
  213. {wavexis-2.2.4 → wavexis-2.2.6}/wavexis/serve.py +0 -0
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to wavexis are documented in this file.
4
4
 
5
+ ## v2.2.6 — 2026-07-07
6
+
7
+ ### Added
8
+
9
+ - Named sessions: `--name` flag stores sessions in `~/.wavexis/sessions/`
10
+ - `wavexis session list` — list all saved named sessions with size and modification date
11
+ - `wavexis session delete --name <name>` — delete a named session
12
+ - Named sessions persist between runs, enabling stateful workflows
13
+
14
+ ## v2.2.5 — 2026-07-07
15
+
16
+ ### Improved
17
+
18
+ - Error messages now include actionable hints for common failures
19
+ - `ElementNotFoundError` suggests `wavexis dom` and `wavexis screenshot` for debugging
20
+ - `WaitTimeoutError` suggests increasing `--timeout` and alternative wait strategies
21
+ - `NavigationError` suggests checking URL, `--timeout`, and `--proxy`
22
+ - `SessionNotInitializedError` suggests `--headed` and checking Chromium installation
23
+ - `BackendNotAvailableError` shows install commands for both backends
24
+ - `MultiConfigError` suggests `--dry-run` to validate config
25
+
5
26
  ## v2.2.4 — 2026-07-07
6
27
 
7
28
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavexis
3
- Version: 2.2.4
3
+ Version: 2.2.6
4
4
  Summary: Browser automation CLI — wraps cdpwave and bidiwave, no Node.js, no Chromium download
5
5
  Project-URL: Homepage, https://github.com/MathiasPaulenko/wavexis
6
6
  Project-URL: Repository, https://github.com/MathiasPaulenko/wavexis
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "wavexis"
7
- version = "2.2.4"
7
+ version = "2.2.6"
8
8
  description = "Browser automation CLI — wraps cdpwave and bidiwave, no Node.js, no Chromium download"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,3 +1,3 @@
1
1
  """wavexis — CLI de automatización de navegador."""
2
2
 
3
- __version__ = "2.2.4"
3
+ __version__ = "2.2.6"
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
+ import time
6
7
  from pathlib import Path
7
8
  from typing import Any
8
9
 
@@ -21,7 +22,7 @@ from wavexis.config import WaitStrategy
21
22
  @app.command()
22
23
  def session(
23
24
  action: str = typer.Argument(
24
- ..., help="Session action: save, load"
25
+ ..., help="Session action: save, load, list, delete"
25
26
  ),
26
27
  url: str = typer.Argument(
27
28
  "", help="URL to navigate to (for save) or load before navigating (for load)"
@@ -29,18 +30,60 @@ def session(
29
30
  output: str = typer.Option(
30
31
  "session.json", "--output", "-o", help="Session file path"
31
32
  ),
33
+ name: str = typer.Option(
34
+ "", "--name", "-n", help="Named session (stored in ~/.wavexis/sessions/)"
35
+ ),
32
36
  ) -> None:
33
37
  """Save or load browser session state (cookies + localStorage + sessionStorage).
34
38
 
35
39
  \b
36
40
  Save: wavexis session save https://app.com -o mysession.json
37
41
  Load: wavexis session load mysession.json https://app.com
42
+ Named: wavexis session save https://app.com --name mysession
43
+ List: wavexis session list
44
+ Delete: wavexis session delete --name mysession
38
45
  """
39
46
  from wavexis.actions.session import SessionLoadAction, SessionSaveAction
40
47
 
41
- session_path = Path(output)
48
+ sessions_dir = Path.home() / ".wavexis" / "sessions"
49
+
50
+ def _session_path() -> Path:
51
+ if name:
52
+ return sessions_dir / f"{name}.json"
53
+ return Path(output)
54
+
55
+ if action == "list":
56
+ if not sessions_dir.exists():
57
+ typer.echo("No saved sessions found.")
58
+ return
59
+ sessions = list(sessions_dir.glob("*.json"))
60
+ if not sessions:
61
+ typer.echo("No saved sessions found.")
62
+ return
63
+ typer.echo(f"Saved sessions ({len(sessions)}):")
64
+ for s in sorted(sessions):
65
+ stat = s.stat()
66
+ modified = time.strftime("%Y-%m-%d %H:%M", time.localtime(stat.st_mtime))
67
+ typer.echo(f" {s.stem} ({stat.st_size} bytes, {modified})")
68
+ return
69
+
70
+ if action == "delete":
71
+ if not name:
72
+ typer.echo("Error: --name required for delete", err=True)
73
+ raise typer.Exit(1)
74
+ target = _session_path()
75
+ if not target.exists():
76
+ typer.echo(f"Error: session '{name}' not found", err=True)
77
+ raise typer.Exit(1)
78
+ target.unlink()
79
+ typer.echo(f"Deleted session '{name}'")
80
+ return
81
+
82
+ session_path = _session_path()
42
83
 
43
84
  if action == "save":
85
+ if name:
86
+ sessions_dir.mkdir(parents=True, exist_ok=True)
44
87
  if not url:
45
88
  typer.echo("Error: URL required for session save", err=True)
46
89
  raise typer.Exit(1)
@@ -56,11 +99,11 @@ def session(
56
99
  await backend.close()
57
100
 
58
101
  _run_async(_save_session())
59
- typer.echo(f"Session saved to {output}")
102
+ typer.echo(f"Session saved to {session_path}")
60
103
 
61
104
  elif action == "load":
62
105
  if not session_path.exists():
63
- typer.echo(f"Error: session file not found: {output}", err=True)
106
+ typer.echo(f"Error: session file not found: {session_path}", err=True)
64
107
  raise typer.Exit(1)
65
108
 
66
109
  async def _load_session() -> Any:
@@ -80,10 +123,13 @@ def session(
80
123
  result = _run_async(_load_session())
81
124
  if result is None:
82
125
  return
83
- typer.echo(f"Session loaded from {output}: {result}")
126
+ typer.echo(f"Session loaded from {session_path}: {result}")
84
127
 
85
128
  else:
86
- typer.echo(f"Error: unknown session action '{action}'. Use save or load.", err=True)
129
+ typer.echo(
130
+ f"Error: unknown session action '{action}'. Use save, load, list, or delete.",
131
+ err=True,
132
+ )
87
133
  raise typer.Exit(1)
88
134
 
89
135
 
@@ -21,6 +21,7 @@ from wavexis.exceptions import (
21
21
  ElementNotFoundError,
22
22
  MultiConfigError,
23
23
  NavigationError,
24
+ SessionNotInitializedError,
24
25
  WaitTimeoutError,
25
26
  WavexisError,
26
27
  )
@@ -39,6 +40,7 @@ __all__ = [
39
40
  "MultiConfigError",
40
41
  "NavigationError",
41
42
  "Output",
43
+ "SessionNotInitializedError",
42
44
  "WaitTimeoutError",
43
45
  "WavexisError",
44
46
  "_browser_options",
@@ -209,20 +211,28 @@ def _handle_error(e: Exception) -> None:
209
211
  """
210
212
  if isinstance(e, BackendNotAvailableError):
211
213
  Output.error(
212
- "No backend available. Install cdpwave: pip install wavexis[cdp]"
214
+ "No backend available.\n"
215
+ "Hint: Install cdpwave with `pip install wavexis[cdp]`, "
216
+ "or bidiwave with `pip install wavexis[bidi]`."
213
217
  )
214
218
  raise typer.Exit(EXIT_BACKEND_ERROR) from e
219
+ if isinstance(e, SessionNotInitializedError):
220
+ Output.error(str(e))
221
+ raise typer.Exit(EXIT_BROWSER_ERROR) from e
215
222
  if isinstance(e, NavigationError):
216
- Output.error(f"Navigation failed: {e}")
223
+ Output.error(str(e))
217
224
  raise typer.Exit(EXIT_BROWSER_ERROR) from e
218
225
  if isinstance(e, WaitTimeoutError):
219
- Output.error(f"Timeout waiting: {e}")
226
+ Output.error(str(e))
220
227
  raise typer.Exit(EXIT_BROWSER_ERROR) from e
221
228
  if isinstance(e, ElementNotFoundError):
222
- Output.error(f"Element not found: {e}")
229
+ Output.error(str(e))
223
230
  raise typer.Exit(EXIT_BROWSER_ERROR) from e
224
231
  if isinstance(e, MultiConfigError):
225
- Output.error(f"Invalid multi config: {e}")
232
+ Output.error(
233
+ f"Invalid multi config: {e}\n"
234
+ "Hint: Run `wavexis multi <config> --dry-run` to validate the config."
235
+ )
226
236
  raise typer.Exit(EXIT_CONFIG_ERROR) from e
227
237
  if isinstance(e, WavexisError):
228
238
  Output.error(str(e))
@@ -41,6 +41,21 @@ class BackendNotSupportedError(WavexisError):
41
41
  class SessionNotInitializedError(WavexisError):
42
42
  """Raised when a backend method is called before launch()."""
43
43
 
44
+ def __init__(self, msg: str | None = None) -> None:
45
+ """Initialize the error.
46
+
47
+ Args:
48
+ msg: Optional custom message.
49
+ """
50
+ if msg:
51
+ super().__init__(msg)
52
+ else:
53
+ super().__init__(
54
+ "Browser session not initialized. Call launch() first.\n"
55
+ "Hint: This usually means the browser failed to start. "
56
+ "Try --headed to see the browser window, or check if Chromium is installed."
57
+ )
58
+
44
59
 
45
60
  class NavigationError(WavexisError):
46
61
  """Raised when navigation fails or times out."""
@@ -52,7 +67,11 @@ class NavigationError(WavexisError):
52
67
  url: The URL that failed to navigate to.
53
68
  reason: The failure reason.
54
69
  """
55
- super().__init__(f"Navigation to '{url}' failed: {reason}")
70
+ super().__init__(
71
+ f"Navigation to '{url}' failed: {reason}\n"
72
+ f"Hint: Check the URL is reachable, try --timeout 60000, "
73
+ f"or use --proxy if behind a firewall."
74
+ )
56
75
 
57
76
 
58
77
  class WaitTimeoutError(WavexisError):
@@ -66,7 +85,9 @@ class WaitTimeoutError(WavexisError):
66
85
  timeout_ms: The timeout duration in milliseconds.
67
86
  """
68
87
  super().__init__(
69
- f"Wait strategy '{strategy}' timed out after {timeout_ms}ms"
88
+ f"Wait strategy '{strategy}' timed out after {timeout_ms}ms\n"
89
+ f"Hint: Increase timeout with --timeout {max(timeout_ms * 2, 60000)}, "
90
+ f"or use a different wait strategy (load, domcontentloaded, networkidle)."
70
91
  )
71
92
 
72
93
 
@@ -79,7 +100,11 @@ class ElementNotFoundError(WavexisError):
79
100
  Args:
80
101
  selector: The CSS selector that matched no elements.
81
102
  """
82
- super().__init__(f"No element found matching selector: {selector}")
103
+ super().__init__(
104
+ f"No element found matching selector: {selector}\n"
105
+ f"Hint: Verify the selector with `wavexis dom --selector '{selector}'`, "
106
+ f"or use `wavexis screenshot` to inspect the page visually."
107
+ )
83
108
 
84
109
 
85
110
  class ActionError(WavexisError):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes