aetherius 0.5.2__tar.gz → 0.5.4__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 (327) hide show
  1. {aetherius-0.5.2 → aetherius-0.5.4}/CHANGELOG.md +144 -1
  2. {aetherius-0.5.2 → aetherius-0.5.4}/PKG-INFO +48 -7
  3. {aetherius-0.5.2 → aetherius-0.5.4}/README.md +46 -5
  4. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/README.md +6 -0
  5. aetherius-0.5.4/conformance/cases/run/17-unreachable-source.json +56 -0
  6. aetherius-0.5.4/conformance/cases/run/18-text-body-and-charset.json +140 -0
  7. aetherius-0.5.4/conformance/cases/validation/37-text-extract-with-path.json +29 -0
  8. {aetherius-0.5.2 → aetherius-0.5.4}/contracts/actions.json +1 -1
  9. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/actions.py +32 -5
  10. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/bridge.py +5 -4
  11. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/driver.py +9 -1
  12. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/data.py +2 -1
  13. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/validator.py +35 -0
  14. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/extraction/dispatch.py +21 -2
  15. aetherius-0.5.4/src/aetherius/core/extraction/text_extractor.py +81 -0
  16. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/version.py +1 -1
  17. {aetherius-0.5.2 → aetherius-0.5.4}/.dockerignore +0 -0
  18. {aetherius-0.5.2 → aetherius-0.5.4}/.env.example +0 -0
  19. {aetherius-0.5.2 → aetherius-0.5.4}/.github/workflows/ci.yml +0 -0
  20. {aetherius-0.5.2 → aetherius-0.5.4}/.github/workflows/release.yml +0 -0
  21. {aetherius-0.5.2 → aetherius-0.5.4}/.gitignore +0 -0
  22. {aetherius-0.5.2 → aetherius-0.5.4}/CONTRIBUTING.md +0 -0
  23. {aetherius-0.5.2 → aetherius-0.5.4}/LICENSE +0 -0
  24. {aetherius-0.5.2 → aetherius-0.5.4}/Makefile +0 -0
  25. {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/UKIT.king.blueprint.json +0 -0
  26. {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/direct.linkaetherius.blueprint.json +0 -0
  27. {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/github.aetherius.blueprint.json +0 -0
  28. {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/github.github.blueprint.json +0 -0
  29. {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/github.zeb.blueprint.json +0 -0
  30. {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/licence.aetherius.blueprint.json +0 -0
  31. {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/record.aetherius.blueprint.json +0 -0
  32. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/01-bare-expression-keeps-the-list.json +0 -0
  33. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/02-text-around-an-expression-gives-a-string.json +0 -0
  34. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/03-python-str-for-bool-and-none.json +0 -0
  35. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/04-recursion-into-arrays-and-objects.json +0 -0
  36. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/05-date-filters.json +0 -0
  37. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/06-filter-binds-tighter-than-not.json +0 -0
  38. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/07-filter-binds-tighter-than-comparison.json +0 -0
  39. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/08-membership-and-boolean-logic.json +0 -0
  40. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/09-inline-conditional-and-is-defined.json +0 -0
  41. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/10-undefined-variable-raises.json +0 -0
  42. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/11-undefined-path-in-text-raises.json +0 -0
  43. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/12-string-that-starts-and-ends-with-an-expression.json +0 -0
  44. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/13-several-expressions-with-surrounding-text.json +0 -0
  45. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/14-unknown-filter-is-a-limit.json +0 -0
  46. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/15-date-outside-iso-is-a-limit.json +0 -0
  47. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/16-dict-method-shadows-key.json +0 -0
  48. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/17-default-boolean-mode.json +0 -0
  49. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/01-jsonpath-list-with-fields.json +0 -0
  50. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/02-single-match-is-still-a-list.json +0 -0
  51. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/03-fields-with-zero-one-and-many-matches.json +0 -0
  52. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/04-quoted-field-name.json +0 -0
  53. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/05-where-on-an-absent-field-raises.json +0 -0
  54. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/06-where-refuses-a-function-call.json +0 -0
  55. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/07-where-refuses-dunder-access.json +0 -0
  56. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/08-where-refuses-subscripting.json +0 -0
  57. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/09-html-css-with-text-pseudo-element.json +0 -0
  58. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/10-html-attributes-and-outer-html.json +0 -0
  59. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/11-jsonpath-filter-is-a-limit.json +0 -0
  60. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/12-recursive-wildcard-is-a-limit.json +0 -0
  61. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/13-where-on-a-nested-field.json +0 -0
  62. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/14-where-on-an-absent-nested-field-raises.json +0 -0
  63. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/15-where-boolean-literal-spelling.json +0 -0
  64. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/01-vector-request-and-extract.json +0 -0
  65. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/02-form-and-query-encoding.json +0 -0
  66. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/03-json-body.json +0 -0
  67. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/04-when-guard-skips.json +0 -0
  68. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/05-flow-if-and-for-each.json +0 -0
  69. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/06-expect-status-violated.json +0 -0
  70. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/07-json-and-form-refused.json +0 -0
  71. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/08-html-extraction.json +0 -0
  72. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/09-unattended-confirm.json +0 -0
  73. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/10-session-cookie-between-steps.json +0 -0
  74. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/11-continuum-navigate-and-extract.json +0 -0
  75. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/12-continuum-form-login.json +0 -0
  76. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/13-continuum-wait-for-named-failure.json +0 -0
  77. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/14-continuum-navigate-status.json +0 -0
  78. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/15-continuum-evaluate-and-flow.json +0 -0
  79. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/16-confirm-timeout-fails.json +0 -0
  80. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/truthy/01-truthy-table.json +0 -0
  81. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/01-vector-minimal.json +0 -0
  82. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/02-parse-malformed-json.json +0 -0
  83. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/03-schema-unknown-act.json +0 -0
  84. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/04-schema-step-without-action.json +0 -0
  85. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/05-model-empty-steps.json +0 -0
  86. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/06-wrong-act-click-in-vector.json +0 -0
  87. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/07-nested-branch-path.json +0 -0
  88. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/08-nested-inherits-step-act.json +0 -0
  89. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/10-not-portable-upload.json +0 -0
  90. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/11-not-portable-drag.json +0 -0
  91. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/12-not-portable-screenshot.json +0 -0
  92. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/13-not-portable-notify.json +0 -0
  93. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/14-act-oracle-not-embeddable.json +0 -0
  94. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/15-step-act-oracle-not-embeddable.json +0 -0
  95. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/16-goal-only-phantom.json +0 -0
  96. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/17-goal-only-wrong-act.json +0 -0
  97. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/18-unknown-action.json +0 -0
  98. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/20-example-vector-flow.json +0 -0
  99. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/21-example-vector-confirm.json +0 -0
  100. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/22-example-continuum-login.json +0 -0
  101. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/23-example-continuum-screenshot.json +0 -0
  102. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/24-example-vector-notify.json +0 -0
  103. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/25-example-oracle-vision.json +0 -0
  104. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/26-example-phantom-goal.json +0 -0
  105. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/27-example-composition-mixed.json +0 -0
  106. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/30-not-portable-xpath-extract.json +0 -0
  107. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/31-example-reference-annonces.json +0 -0
  108. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/32-example-reference-restaurants.json +0 -0
  109. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/33-example-reference-affluence.json +0 -0
  110. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/34-example-reference-celcat.json +0 -0
  111. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/35-example-reference-sso.json +0 -0
  112. {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/36-example-reference-messagerie.json +0 -0
  113. {aetherius-0.5.2 → aetherius-0.5.4}/contracts/blueprint.schema.json +0 -0
  114. {aetherius-0.5.2 → aetherius-0.5.4}/contracts/events.schema.json +0 -0
  115. {aetherius-0.5.2 → aetherius-0.5.4}/contracts/openapi.yaml +0 -0
  116. {aetherius-0.5.2 → aetherius-0.5.4}/deploy/.env.example +0 -0
  117. {aetherius-0.5.2 → aetherius-0.5.4}/deploy/Dockerfile +0 -0
  118. {aetherius-0.5.2 → aetherius-0.5.4}/deploy/aetherius.service +0 -0
  119. {aetherius-0.5.2 → aetherius-0.5.4}/deploy/blueprints/.gitkeep +0 -0
  120. {aetherius-0.5.2 → aetherius-0.5.4}/deploy/docker-compose.yml +0 -0
  121. {aetherius-0.5.2 → aetherius-0.5.4}/docs-ukit/README.md +0 -0
  122. {aetherius-0.5.2 → aetherius-0.5.4}/pyproject.toml +0 -0
  123. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/__init__.py +0 -0
  124. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/__main__.py +0 -0
  125. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/_contracts/__init__.py +0 -0
  126. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/_contracts/blueprint.schema.json +0 -0
  127. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/__init__.py +0 -0
  128. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/__init__.py +0 -0
  129. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/claude.py +0 -0
  130. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/local.py +0 -0
  131. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/planning.py +0 -0
  132. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/provider.py +0 -0
  133. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_perception.py +0 -0
  134. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_shared.py +0 -0
  135. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/__init__.py +0 -0
  136. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/browser.py +0 -0
  137. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/debug_overlay.py +0 -0
  138. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/driver.py +0 -0
  139. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/human_actions.py +0 -0
  140. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/__init__.py +0 -0
  141. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/driver.py +0 -0
  142. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/locator.py +0 -0
  143. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/model.py +0 -0
  144. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/perception.py +0 -0
  145. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/scan.py +0 -0
  146. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/__init__.py +0 -0
  147. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/driver.py +0 -0
  148. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/loop.py +0 -0
  149. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/memory.py +0 -0
  150. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/perception.py +0 -0
  151. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/planner.py +0 -0
  152. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/__init__.py +0 -0
  153. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/auth.py +0 -0
  154. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/client.py +0 -0
  155. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/impersonate.py +0 -0
  156. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/__init__.py +0 -0
  157. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/catalog.py +0 -0
  158. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/factory.py +0 -0
  159. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/templates.py +0 -0
  160. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/validation.py +0 -0
  161. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/cli/__init__.py +0 -0
  162. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/cli/_shared.py +0 -0
  163. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/cli/approvals.py +0 -0
  164. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/cli/schedule.py +0 -0
  165. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/config/__init__.py +0 -0
  166. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/config/secrets.py +0 -0
  167. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/config/settings.py +0 -0
  168. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/__init__.py +0 -0
  169. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/app.py +0 -0
  170. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/approvals.py +0 -0
  171. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/console.tcss +0 -0
  172. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/daemon_control.py +0 -0
  173. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/run_bridge.py +0 -0
  174. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/__init__.py +0 -0
  175. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/_pending.py +0 -0
  176. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/__init__.py +0 -0
  177. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/act_picker.py +0 -0
  178. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/io_editor.py +0 -0
  179. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/options_editor.py +0 -0
  180. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/preview.py +0 -0
  181. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/screen.py +0 -0
  182. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/step_editor.py +0 -0
  183. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/catalog.py +0 -0
  184. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/home.py +0 -0
  185. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/library.py +0 -0
  186. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/library_scan.py +0 -0
  187. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/recorder.py +0 -0
  188. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/runs.py +0 -0
  189. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/__init__.py +0 -0
  190. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/_common.py +0 -0
  191. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/detail.py +0 -0
  192. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/form.py +0 -0
  193. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/screen.py +0 -0
  194. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/sessions.py +0 -0
  195. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/settings.py +0 -0
  196. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screenshots.py +0 -0
  197. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/theme.py +0 -0
  198. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/__init__.py +0 -0
  199. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/confirm.py +0 -0
  200. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/event_log.py +0 -0
  201. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/form.py +0 -0
  202. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/json_preview.py +0 -0
  203. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/run_summary.py +0 -0
  204. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/__init__.py +0 -0
  205. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/__init__.py +0 -0
  206. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/base.py +0 -0
  207. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/contract.py +0 -0
  208. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/flow.py +0 -0
  209. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/human.py +0 -0
  210. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/interaction.py +0 -0
  211. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/navigation.py +0 -0
  212. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/notification.py +0 -0
  213. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/registry.py +0 -0
  214. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/spec.py +0 -0
  215. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/vision.py +0 -0
  216. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/__init__.py +0 -0
  217. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/loader.py +0 -0
  218. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/models.py +0 -0
  219. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/template.py +0 -0
  220. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/driver.py +0 -0
  221. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/errors.py +0 -0
  222. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/events/__init__.py +0 -0
  223. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/events/bus.py +0 -0
  224. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/events/models.py +0 -0
  225. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/events/sinks.py +0 -0
  226. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/extraction/__init__.py +0 -0
  227. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/extraction/html_extractor.py +0 -0
  228. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/extraction/json_extractor.py +0 -0
  229. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/__init__.py +0 -0
  230. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/approvals.py +0 -0
  231. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/context.py +0 -0
  232. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/drivers.py +0 -0
  233. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/engine.py +0 -0
  234. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/flow.py +0 -0
  235. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/healing.py +0 -0
  236. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/result.py +0 -0
  237. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/selector.py +0 -0
  238. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/steps.py +0 -0
  239. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/models/__init__.py +0 -0
  240. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/models/registry.py +0 -0
  241. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/models/store/.gitkeep +0 -0
  242. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/__init__.py +0 -0
  243. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/geo.py +0 -0
  244. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/identity.py +0 -0
  245. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/pool.py +0 -0
  246. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/proxy.py +0 -0
  247. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/transport.py +0 -0
  248. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/__init__.py +0 -0
  249. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/base.py +0 -0
  250. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/__init__.py +0 -0
  251. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/_http.py +0 -0
  252. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/discord.py +0 -0
  253. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/ntfy.py +0 -0
  254. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/telegram.py +0 -0
  255. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/webhook.py +0 -0
  256. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/message.py +0 -0
  257. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/registry.py +0 -0
  258. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/sink.py +0 -0
  259. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/plugins.py +0 -0
  260. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/__init__.py +0 -0
  261. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_capture_js.py +0 -0
  262. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_gesture_js.py +0 -0
  263. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_names.py +0 -0
  264. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_overlay_js.py +0 -0
  265. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_playwright.py +0 -0
  266. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_selector_js.py +0 -0
  267. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_transform.py +0 -0
  268. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_vector_js.py +0 -0
  269. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/base.py +0 -0
  270. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/blueprint_recorder.py +0 -0
  271. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/capture.py +0 -0
  272. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/continuum_backend.py +0 -0
  273. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/gesture_recorder.py +0 -0
  274. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/selector_synth.py +0 -0
  275. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/session.py +0 -0
  276. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/vector_backend.py +0 -0
  277. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/__init__.py +0 -0
  278. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/app.py +0 -0
  279. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/approvals.py +0 -0
  280. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/config.py +0 -0
  281. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/deps.py +0 -0
  282. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/jobs.py +0 -0
  283. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/__init__.py +0 -0
  284. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/blueprints.py +0 -0
  285. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/recorder.py +0 -0
  286. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/runs.py +0 -0
  287. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/schedules.py +0 -0
  288. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/stream.py +0 -0
  289. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/__init__.py +0 -0
  290. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/alerts.py +0 -0
  291. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/manual.py +0 -0
  292. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/misfire.py +0 -0
  293. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/service.py +0 -0
  294. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/triggers.py +0 -0
  295. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/schemas.py +0 -0
  296. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/__init__.py +0 -0
  297. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/__init__.py +0 -0
  298. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/hardening.py +0 -0
  299. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/headers.py +0 -0
  300. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/patch.py +0 -0
  301. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/profile.py +0 -0
  302. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/webrtc.py +0 -0
  303. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/gestures/__init__.py +0 -0
  304. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/gestures/data/human_library.json +0 -0
  305. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/gestures/library.py +0 -0
  306. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/gestures/seed.py +0 -0
  307. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/__init__.py +0 -0
  308. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/input.py +0 -0
  309. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/keyboard.py +0 -0
  310. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/mouse.py +0 -0
  311. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/scroll.py +0 -0
  312. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/timing.py +0 -0
  313. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/ml/__init__.py +0 -0
  314. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/ml/fingerprint_model.py +0 -0
  315. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/ml/motion_model.py +0 -0
  316. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/policy.py +0 -0
  317. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/session/__init__.py +0 -0
  318. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/session/store.py +0 -0
  319. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/session/warmup.py +0 -0
  320. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/__init__.py +0 -0
  321. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/approvals.py +0 -0
  322. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/engine.py +0 -0
  323. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/models.py +0 -0
  324. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/runs.py +0 -0
  325. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/schedules.py +0 -0
  326. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/schema.py +0 -0
  327. {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/state.py +0 -0
@@ -5,6 +5,147 @@ Toutes les évolutions notables du projet sont consignées ici. Le format s'insp
5
5
  [SemVer](https://semver.org/lang/fr/). Tant que la version reste en `0.x`, l'API publique peut encore
6
6
  évoluer entre deux versions mineures.
7
7
 
8
+ ## [0.5.4] - 2026-08-13
9
+
10
+ Un second appendice à la Phase 3, ouvert par le même port réel que le premier : l'extraction d'Act I
11
+ ne connaissait que le JSON et le HTML, si bien qu'une réponse **à lignes** — iCalendar, CSV,
12
+ `text/plain` — était hors de portée d'un Blueprint. Elle gagne sa troisième et dernière forme,
13
+ `from: "text"`, dans les deux moteurs. Une valeur d'énumération, et rien d'autre.
14
+
15
+ ### Ajouté
16
+ - **Jalon 3-I — Extraire un corps de réponse en texte**
17
+ ([docs/acts/vector.md](docs/acts/vector.md#from-text--les-formats-à-lignes),
18
+ [docs/phase-3/3-i-extraction-texte.md](docs/phase-3/3-i-extraction-texte.md)). Le besoin vient de
19
+ [UKit](docs-ukit/README.md) : à peu près aucune université française n'expose un serveur de
20
+ planning interrogeable sans authentification, alors que presque toutes offrent un **export iCal**.
21
+ C'est la seule voie qui ne demande pas de porter un produit de planning par établissement.
22
+ - **`extract: { x: { from: "text" } }`** rend le corps décodé, tel quel. Pas de parseur, et pas de
23
+ `from: "regex"` : une seconde grammaire d'expressions régulières entre Python et JavaScript
24
+ finirait par diverger, et le filtrage d'un texte reste applicatif.
25
+ - **Le corps n'est jamais publié tout seul.** `http.request` continue de ne rendre que
26
+ `status_code` et `headers` : sans extraction nommée, la charge utile ne traîne ni dans les
27
+ journaux, ni dans les événements, ni dans la mémoire d'un run qui n'en a que faire. C'est
28
+ l'extraction qui dit **ce qu'on garde**.
29
+ - **Le décodage suit l'en-tête de réponse**, avec repli UTF-8 et remplacement des octets invalides
30
+ — un corps binaire ne lève pas : ce n'est pas au moteur de deviner qu'on s'est trompé de source.
31
+ Un BOM est conservé, comme le fait le codec Python.
32
+ - **La table d'encodages est bornée et partagée par les deux moteurs** (latin-1 **strict**, cp1252,
33
+ UTF-8 pour toute autre étiquette) plutôt que déléguée à leurs plateformes. C'est la décision
34
+ centrale du jalon : `TextDecoder` est absent de React Native et complet sous Node, donc la
35
+ solution paresseuse aurait mis la CI d'accord et le téléphone en désaccord — sur la première
36
+ source mal étiquetée, en production. Le moteur embarqué porte donc son décodeur UTF-8
37
+ (algorithme WHATWG, soit un `U+FFFD` par sous-partie maximale, ce que rend `errors="replace"` de
38
+ CPython) et sa table mono-octet, **sans dépendance ajoutée**.
39
+ - **Sur l'appareil, les octets ne sont lus que si un `from: "text"` est déclaré** : le driver
40
+ scanne le bloc `extract` avant d'envoyer la requête — lecture statique légitime, les specs
41
+ n'étant jamais rendues — et n'appelle `arrayBuffer()` que dans ce cas. Une requête sans
42
+ extraction texte emprunte exactement le chemin d'avant et ne paie pas le pont base64 de React
43
+ Native ; un hôte dont la réponse ne sait pas rendre ses octets obtient une erreur **typée qui le
44
+ nomme**, jamais un décodage UTF-8 tacite ni une erreur réseau trompeuse.
45
+ - **`path`, `where`, `fields`, `selector`, `selector_type`, `attr` et `multiple` sont refusés** avec
46
+ `from: "text"`, **à la validation** et par les deux moteurs : un Blueprint qui croit filtrer est
47
+ pire qu'un Blueprint qui échoue. La règle vit dans les deux validateurs sémantiques, pas dans la
48
+ couche de portabilité du moteur embarqué — elle appartient au contrat.
49
+ - **Corpus de conformance** : `run/18-text-body-and-charset` sert quatre routes (latin-1 bien
50
+ étiqueté, UTF-8 **mal** étiqueté — les deux moteurs doivent produire le *même* mojibake —,
51
+ `Content-Type` sans `charset`, corps vide qui rend `""` et non `null`), ce qui a demandé un champ
52
+ de route **`charset`** aux deux serveurs de fixtures ; `validation/37-text-extract-with-path`
53
+ fige le refus. Les tests unitaires ajoutent une table d'octets UTF-8 invalides dont les valeurs
54
+ attendues viennent de CPython et sont recopiées dans le test JavaScript : c'est ce qui garde les
55
+ deux décodeurs alignés sur les cas dégénérés qu'un serveur ne peut pas facilement servir.
56
+ - **Exemple exécutable zéro configuration** :
57
+ [`examples/vector/ical-planning-text`](examples/vector/ical-planning-text.blueprint.json)
58
+ (export iCal anonyme d'un vrai serveur ADE, entrées figées sur une plage passée dont le contenu
59
+ reste servi — donc reproductible sans attendre une rentrée), avec sa **contre-épreuve conçue pour
60
+ échouer**, [`ical-error-page-probe`](examples/mobile/ical-error-page-probe.blueprint.json) : le
61
+ même export sans paramètres répond 500 avec une page HTML, et la garde de forme doit mordre.
62
+ - **Vérifié sur iPhone** : `caracteres: 21461` et `80712` **identiques** au moteur Python, accents
63
+ compris, et la sonde conçue pour échouer échoue au même step avec le même message. C'est la seule
64
+ vérification qui pouvait couvrir le pont d'octets de React Native (blob → base64 → natif), qui
65
+ n'existe nulle part ailleurs.
66
+ - `contracts/blueprint.schema.json` **ne bouge pas** — le bloc `extract` y était déjà ouvert ; seule
67
+ l'aide du paramètre, dans le contrat **généré** `contracts/actions.json`, nomme désormais les
68
+ trois formes.
69
+
70
+ ### Corrigé
71
+ - **Un rendez-vous d'approbation pouvait s'annoncer non expiré alors qu'il venait de l'être.** Sans
72
+ rapport avec le jalon, trouvé parce que la suite du moteur embarqué échouait une fois sur trois :
73
+ `expired` comparait l'horloge à l'échéance, or un `setTimeout` peut se déclencher une fraction de
74
+ milliseconde avant que `Date.now()` ne l'atteigne. L'attente rendait donc `null` — la décision
75
+ n'arrivera plus — pendant que `expired` répondait encore `false`, deux réponses contradictoires sur
76
+ le même rendez-vous. Le minuteur fait désormais foi en plus de l'horloge. Aucune décision ne
77
+ pouvait être appliquée à tort (`resolve` était déjà gardé par l'état « résolu »), mais une surface
78
+ qui lit `expired` pour cesser d'afficher sa demande pouvait la laisser à l'écran.
79
+
80
+ ## [0.5.3] - 2026-08-10
81
+
82
+ Un correctif, pas un jalon : la Phase 3 reste close (3-A à 3-H). Une promesse du jalon 3-E — « une
83
+ source injoignable dit qu'elle l'est » — ne tenait pas sur un appareil, et le port d'une application
84
+ réelle l'a montré. Elle tient désormais **des deux côtés**, et trois gardes l'empêchent de repartir.
85
+
86
+ ### Corrigé
87
+ - **Une source injoignable atteint `unavailable`, sur le moteur embarqué**
88
+ ([docs/embedded.md](docs/embedded.md#une-source-injoignable-atteint-unavailable-corrigé-en-053)).
89
+ Mesuré sur iPhone le 2026-08-09 en portant la session universitaire d'
90
+ [UKit](docs-ukit/README.md) : un `navigate` vers une adresse injoignable **réussissait**, et c'est
91
+ l'attente suivante qui échouait — en `blocked` avec le code du Blueprint, ou en `data`. Aucune
92
+ panne réseau de l'Act II n'ouvrait donc de bouton Réessayer, ce qui vidait de son sens le
93
+ paragraphe central du modèle d'erreur.
94
+ - **La cause n'était pas le câblage.** `react-native-webview` tire `onError` **puis** `onLoadEnd`
95
+ pour la *même* navigation en échec, dans le même tick. Le second effaçait le verdict, avançait
96
+ la génération et réinjectait l'agent — qui s'annonçait depuis la page d'erreur de la plateforme
97
+ comme depuis n'importe quel document. Deux erreurs, et il fallait les deux : un événement de fin
98
+ de chargement pris pour la **preuve** qu'un document s'est chargé, et une boucle d'attente qui
99
+ testait la génération **avant** le verdict alors que les deux sont vrais au même tour.
100
+ - **Deux défauts latents du même chemin**, trouvés en écrivant le correctif et qui mordaient
101
+ précisément sur le bouton Réessayer qu'il rend possible : le verdict n'était effacé que par le
102
+ signal de la vue, qui arrive après le premier tour de la boucle — une reprise échouait donc
103
+ instantanément en héritant d'un échec qu'elle n'avait pas rencontré ; et une reprise sur la même
104
+ URL demandait un `reload()`, que la plateforme ignore sur une vue dont la navigation provisoire
105
+ a échoué. `PageControl.load` exige désormais de charger **même si la vue affiche déjà cette
106
+ URL**, et le composant l'honore en recréant la vue.
107
+ - **Un chargement qui part et ne revient jamais est `unavailable`**, plus `engine` : à l'échéance,
108
+ un chargement que la vue a annoncé sans le terminer lève un `TimeoutError`. C'est le second
109
+ symptôme que la campagne d'origine avait observé sans l'élucider — un nom qui ne résout pas.
110
+ - **Le verdict reste borné à la navigation demandée** : `awaitReady` teste toujours la présence de
111
+ l'agent d'abord, parce qu'une navigation de fond qui échoue laisse le document courant intact et
112
+ qu'échouer là ferait régresser des runs qui marchent.
113
+ - **Une source injoignable est une `NetworkError`, sur le moteur Python**
114
+ ([docs/acts/continuum.md](docs/acts/continuum.md#une-source-injoignable-est-une-networkerror)).
115
+ Le même angle mort, trouvé en corrigeant l'autre : `page.goto` vers une adresse injoignable levait
116
+ une `playwright.Error` brute, enveloppée en `RunError` — un bug du moteur, pour qui lit le
117
+ `Result`. `navigate`, `back`, `forward` et `reload` typent maintenant un échec de transport
118
+ (`net::ERR_*`, le code de Chromium, seul navigateur que cet Act lance). La détection est étroite
119
+ par choix : une page qui se ferme, un `frame` qui se détache ou un `TimeoutError` de Playwright
120
+ gardent leur chemin — une page lente n'est pas une page qu'on n'atteint pas.
121
+
122
+ ### Ajouté
123
+ - **Un cas de conformance partagé** (`conformance/cases/run/17-unreachable-source.json`) : les deux
124
+ moteurs échouent **au step `navigate`**, et le step suivant ne démarre pas. C'est exactement la
125
+ séquence qui divergeait, donc c'est la bonne garde anti-dérive.
126
+ - **Le double jsdom émet `onLoadFailed` avant `onDocumentLoaded`**, comme la plateforme. C'est la
127
+ leçon de méthode du correctif : le corpus jouait ce chemin depuis deux jalons et le déclarait vert
128
+ parce que le double ne reproduisait que la moitié de la séquence — celle qui ne contient pas le
129
+ bug. Un double qui n'émet pas les signaux d'une plateforme ne peut rien garder.
130
+ - **Une sonde de banc, `examples/mobile/unreachable-probe.blueprint.json`**, conçue pour échouer :
131
+ déterministe, hors ligne, elle **remplace le passage en mode avion** du parcours de vérification —
132
+ rien à changer sur le téléphone.
133
+
134
+ ### Connu
135
+ - **Un échec de chargement que la vue ne rapporte pas reste invisible**, et c'est la première passe
136
+ sur appareil qui l'a montré : le correctif était en place, gardé, et la sonde rendait quand même
137
+ `PAGE_ABSENTE`. `react-native-webview` filtre `WebKitErrorDomain` **101** (« cannot show URL »)
138
+ avant d'appeler `onError`, or c'est ce que WebKit rend pour un **port bloqué** — et la sonde visait
139
+ le port 1. La vue savait, la bibliothèque a avalé, l'hôte est resté aveugle. Le signe avant-coureur
140
+ était sous les yeux : côté Python la même adresse donnait `net::ERR_UNSAFE_PORT`, pas
141
+ `ERR_CONNECTION_REFUSED`.
142
+ - **Règle de sondage : viser un port qui _refuse_ (4), pas un port _bloqué_ (1, 7, 9 …).** La
143
+ sonde, le cas de conformance et le test navigateur l'appliquent.
144
+ - Le périmètre restant n'est pas celui de la production : un portail en panne rend
145
+ `NSURLErrorDomain` (-1001, -1004, -1009), que la bibliothèque laisse passer. Ce qu'elle avale —
146
+ port bloqué, schéma non supporté — désigne des Blueprints à corriger, pas des services à
147
+ réessayer.
148
+
8
149
  ## [0.5.2] - 2026-08-07
9
150
 
10
151
  Un appendice à la Phase 3, ouvert par un port réel : la livraison distante sait désormais
@@ -965,7 +1106,9 @@ Première release publique. Elle clôt la **Phase 1** : le socle d'Aetherius, ut
965
1106
  - SemVer `0.x` : l'API peut évoluer pendant le durcissement de la Phase 1.
966
1107
  - La **Phase 2** ajoutera Act III (Oracle, vision) et Act IV (Phantom, agent autonome).
967
1108
 
968
- [Non publié]: https://github.com/kln-mltre/Aetherius/compare/v0.5.2...HEAD
1109
+ [Non publié]: https://github.com/kln-mltre/Aetherius/compare/v0.5.3...HEAD
1110
+ [0.5.4]: https://github.com/kln-mltre/Aetherius/releases/tag/v0.5.4
1111
+ [0.5.3]: https://github.com/kln-mltre/Aetherius/releases/tag/v0.5.3
969
1112
  [0.5.2]: https://github.com/kln-mltre/Aetherius/releases/tag/v0.5.2
970
1113
  [0.5.1]: https://github.com/kln-mltre/Aetherius/releases/tag/v0.5.1
971
1114
  [0.5.0]: https://github.com/kln-mltre/Aetherius/releases/tag/v0.5.0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: aetherius
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: A fixed, robust web-bot engine driven by declarative instruction files (Blueprints).
5
5
  Project-URL: Repository, https://github.com/kln-mltre/Aetherius
6
6
  Author: Kylian Malartre
@@ -130,8 +130,9 @@ plus les dépendances sont lourdes (installées à la demande via des *extras*).
130
130
  ### Act I — Vector
131
131
  Client HTTP robuste : requêtes GET/POST, encodage form/JSON, en-têtes, retries avec backoff,
132
132
  pagination, stratégies d'authentification (cookie, bearer, basic, form-login type CAS), extraction
133
- déclarative (JSONPath pour le JSON, CSS/XPath pour le HTML). Remplace les services `axios` écrits à
134
- la main ; les constantes magiques disséminées deviennent des `inputs` typés et documentés.
133
+ déclarative (JSONPath pour le JSON, CSS/XPath pour le HTML, le corps décodé pour les formats à
134
+ lignes type iCalendar). Remplace les services `axios` écrits à la main ; les constantes magiques
135
+ disséminées deviennent des `inputs` typés et documentés.
135
136
 
136
137
  ### Act II — Continuum
137
138
  Automatisation d'un vrai navigateur (Playwright) qui suit le Blueprint à la lettre : navigation,
@@ -807,7 +808,8 @@ binaire : un **registre** le résout entre un socle embarqué et une surcouche d
807
808
  **un site qui change se répare sans republier sur les stores**. Le jalon 3-G clôt la phase en
808
809
  **portant un cas d'usage mobile réel** — quatre API tierces et un parcours authentifiant complet —
809
810
  et en livrant le guide de migration ; le jalon 3-H, appendice ouvert par ce port, laisse le même
810
- manifeste **ajouter** un Blueprint sous un préfixe réservé. Référence d'usage :
811
+ manifeste **ajouter** un Blueprint sous un préfixe réservé, et le jalon 3-I, ouvert par le même,
812
+ rend atteignables les réponses qui ne sont ni JSON ni HTML (`from: "text"` — iCalendar, CSV). Référence d'usage :
811
813
  [docs/embedded.md](docs/embedded.md).
812
814
 
813
815
  - [x] **3-A** — Socle TypeScript & parité : le moteur embarqué **charge, valide et refuse** un
@@ -1008,8 +1010,47 @@ manifeste **ajouter** un Blueprint sous un préfixe réservé. Référence d'usa
1008
1010
  [docs/embedded.md](docs/embedded.md#étendre--les-noms-réservés),
1009
1011
  [docs/phase-3/3-h-portails.md](docs/phase-3/3-h-portails.md).
1010
1012
 
1011
- **Phase 3 terminée (A–H).** Le même Blueprint tourne sur une machine et sur un téléphone, se corrige
1012
- et s'ajoute à distance, et un cas d'usage réel le prouve.
1013
+ - [x] **3-I** — **Extraire un corps de réponse en texte.** Second appendice, et la même origine que le
1014
+ premier : un port réel a buté sur une limite du contrat. L'extraction d'Act I ne connaissait que
1015
+ `json` et `html`, et `http.request` publie `status_code` et `headers` mais **jamais le corps** — une
1016
+ réponse qui n'est ni l'un ni l'autre était donc hors de portée d'un Blueprint. C'est le cas de tous
1017
+ les formats à lignes, dont **iCalendar**, et le besoin est concret : à peu près aucune université
1018
+ française n'expose un serveur de planning interrogeable sans authentification, alors que presque
1019
+ toutes offrent un lien d'abonnement. Le jalon ajoute **une valeur d'énumération**, `from: "text"`, et
1020
+ rien d'autre — pas de parseur, pas de `regex` (deux moteurs, un contrat : ce qui n'est pas identique
1021
+ par construction finit par diverger), et **aucune sortie de step ne grossit** : c'est l'extraction
1022
+ nommée qui dit ce qu'on garde, un corps ne traîne pas dans les journaux de ceux qui n'en veulent pas.
1023
+ Le point qui décidait de tout était le **décodage**, et il a été tranché à l'inverse de la facilité :
1024
+ plutôt que chaque moteur délègue à sa plateforme, les deux portent la **même table d'encodages
1025
+ bornée** (latin-1 strict, cp1252, UTF-8 pour tout le reste) et le moteur embarqué écrit ses propres
1026
+ décodeurs — `TextDecoder` est absent de React Native et complet sous Node, s'en servir aurait mis la
1027
+ CI d'accord et le téléphone en désaccord, sur la première source mal étiquetée. Sur l'appareil, les
1028
+ octets ne sont lus (`arrayBuffer`, donc le pont base64 de React Native) **que** si un `from: "text"`
1029
+ est déclaré : une requête qui n'en veut pas emprunte exactement le chemin d'avant. Les clés des
1030
+ autres dialectes (`path`, `where`, `fields`, `selector`, …) sont **refusées à la validation** par les
1031
+ deux moteurs — un Blueprint qui croit filtrer est pire qu'un Blueprint qui échoue. Exemple exécutable
1032
+ zéro configuration : [`examples/vector/ical-planning-text`](examples/vector/ical-planning-text.blueprint.json)
1033
+ (export iCal anonyme d'un vrai serveur ADE), avec sa contre-épreuve conçue pour échouer — la page
1034
+ d'erreur du même serveur, qui ne doit **pas** passer pour un calendrier vide.
1035
+ [docs/acts/vector.md](docs/acts/vector.md#from-text--les-formats-à-lignes),
1036
+ [docs/phase-3/3-i-extraction-texte.md](docs/phase-3/3-i-extraction-texte.md).
1037
+
1038
+ **Phase 3 terminée (A–I).** Le même Blueprint tourne sur une machine et sur un téléphone, se corrige
1039
+ et s'ajoute à distance, et un cas d'usage réel le prouve — et continue de désigner ce qui manque.
1040
+
1041
+ > **Correctif 0.5.3 — une source injoignable atteint enfin `unavailable`.** La phase reste close ;
1042
+ > ceci n'ouvre pas de jalon. Le port d'UKit a trouvé sur iPhone que le signal d'échec de chargement
1043
+ > d'une WebView, câblé depuis le jalon 3-E, n'était **jamais lu** : la plateforme tire un événement
1044
+ > de fin de chargement pour une navigation en échec, l'agent s'installait sur sa page d'erreur, et la
1045
+ > navigation passait pour réussie. Aucune panne réseau de l'Act II n'ouvrait donc de bouton
1046
+ > Réessayer. Le correctif tient en deux règles — un événement de chargement n'efface pas un verdict
1047
+ > d'échec, et le verdict se lit **avant** la génération — plus deux défauts latents du même chemin
1048
+ > (une reprise condamnée par l'échec précédent, un `reload()` sur une vue sans document). Le moteur
1049
+ > **Python** avait le même angle mort et le corrige aussi : `navigate` type un échec de transport en
1050
+ > `NetworkError`. Trois gardes, dont un cas de conformance partagé, et un double jsdom qui reproduit
1051
+ > désormais la séquence de la plateforme — c'est son silence qui avait laissé passer le défaut deux
1052
+ > jalons durant. [docs/embedded.md](docs/embedded.md#une-source-injoignable-atteint-unavailable-corrigé-en-053),
1053
+ > [docs/acts/continuum.md](docs/acts/continuum.md#une-source-injoignable-est-une-networkerror).
1013
1054
 
1014
1055
  ## Sources de référence
1015
1056
 
@@ -57,8 +57,9 @@ plus les dépendances sont lourdes (installées à la demande via des *extras*).
57
57
  ### Act I — Vector
58
58
  Client HTTP robuste : requêtes GET/POST, encodage form/JSON, en-têtes, retries avec backoff,
59
59
  pagination, stratégies d'authentification (cookie, bearer, basic, form-login type CAS), extraction
60
- déclarative (JSONPath pour le JSON, CSS/XPath pour le HTML). Remplace les services `axios` écrits à
61
- la main ; les constantes magiques disséminées deviennent des `inputs` typés et documentés.
60
+ déclarative (JSONPath pour le JSON, CSS/XPath pour le HTML, le corps décodé pour les formats à
61
+ lignes type iCalendar). Remplace les services `axios` écrits à la main ; les constantes magiques
62
+ disséminées deviennent des `inputs` typés et documentés.
62
63
 
63
64
  ### Act II — Continuum
64
65
  Automatisation d'un vrai navigateur (Playwright) qui suit le Blueprint à la lettre : navigation,
@@ -734,7 +735,8 @@ binaire : un **registre** le résout entre un socle embarqué et une surcouche d
734
735
  **un site qui change se répare sans republier sur les stores**. Le jalon 3-G clôt la phase en
735
736
  **portant un cas d'usage mobile réel** — quatre API tierces et un parcours authentifiant complet —
736
737
  et en livrant le guide de migration ; le jalon 3-H, appendice ouvert par ce port, laisse le même
737
- manifeste **ajouter** un Blueprint sous un préfixe réservé. Référence d'usage :
738
+ manifeste **ajouter** un Blueprint sous un préfixe réservé, et le jalon 3-I, ouvert par le même,
739
+ rend atteignables les réponses qui ne sont ni JSON ni HTML (`from: "text"` — iCalendar, CSV). Référence d'usage :
738
740
  [docs/embedded.md](docs/embedded.md).
739
741
 
740
742
  - [x] **3-A** — Socle TypeScript & parité : le moteur embarqué **charge, valide et refuse** un
@@ -935,8 +937,47 @@ manifeste **ajouter** un Blueprint sous un préfixe réservé. Référence d'usa
935
937
  [docs/embedded.md](docs/embedded.md#étendre--les-noms-réservés),
936
938
  [docs/phase-3/3-h-portails.md](docs/phase-3/3-h-portails.md).
937
939
 
938
- **Phase 3 terminée (A–H).** Le même Blueprint tourne sur une machine et sur un téléphone, se corrige
939
- et s'ajoute à distance, et un cas d'usage réel le prouve.
940
+ - [x] **3-I** — **Extraire un corps de réponse en texte.** Second appendice, et la même origine que le
941
+ premier : un port réel a buté sur une limite du contrat. L'extraction d'Act I ne connaissait que
942
+ `json` et `html`, et `http.request` publie `status_code` et `headers` mais **jamais le corps** — une
943
+ réponse qui n'est ni l'un ni l'autre était donc hors de portée d'un Blueprint. C'est le cas de tous
944
+ les formats à lignes, dont **iCalendar**, et le besoin est concret : à peu près aucune université
945
+ française n'expose un serveur de planning interrogeable sans authentification, alors que presque
946
+ toutes offrent un lien d'abonnement. Le jalon ajoute **une valeur d'énumération**, `from: "text"`, et
947
+ rien d'autre — pas de parseur, pas de `regex` (deux moteurs, un contrat : ce qui n'est pas identique
948
+ par construction finit par diverger), et **aucune sortie de step ne grossit** : c'est l'extraction
949
+ nommée qui dit ce qu'on garde, un corps ne traîne pas dans les journaux de ceux qui n'en veulent pas.
950
+ Le point qui décidait de tout était le **décodage**, et il a été tranché à l'inverse de la facilité :
951
+ plutôt que chaque moteur délègue à sa plateforme, les deux portent la **même table d'encodages
952
+ bornée** (latin-1 strict, cp1252, UTF-8 pour tout le reste) et le moteur embarqué écrit ses propres
953
+ décodeurs — `TextDecoder` est absent de React Native et complet sous Node, s'en servir aurait mis la
954
+ CI d'accord et le téléphone en désaccord, sur la première source mal étiquetée. Sur l'appareil, les
955
+ octets ne sont lus (`arrayBuffer`, donc le pont base64 de React Native) **que** si un `from: "text"`
956
+ est déclaré : une requête qui n'en veut pas emprunte exactement le chemin d'avant. Les clés des
957
+ autres dialectes (`path`, `where`, `fields`, `selector`, …) sont **refusées à la validation** par les
958
+ deux moteurs — un Blueprint qui croit filtrer est pire qu'un Blueprint qui échoue. Exemple exécutable
959
+ zéro configuration : [`examples/vector/ical-planning-text`](examples/vector/ical-planning-text.blueprint.json)
960
+ (export iCal anonyme d'un vrai serveur ADE), avec sa contre-épreuve conçue pour échouer — la page
961
+ d'erreur du même serveur, qui ne doit **pas** passer pour un calendrier vide.
962
+ [docs/acts/vector.md](docs/acts/vector.md#from-text--les-formats-à-lignes),
963
+ [docs/phase-3/3-i-extraction-texte.md](docs/phase-3/3-i-extraction-texte.md).
964
+
965
+ **Phase 3 terminée (A–I).** Le même Blueprint tourne sur une machine et sur un téléphone, se corrige
966
+ et s'ajoute à distance, et un cas d'usage réel le prouve — et continue de désigner ce qui manque.
967
+
968
+ > **Correctif 0.5.3 — une source injoignable atteint enfin `unavailable`.** La phase reste close ;
969
+ > ceci n'ouvre pas de jalon. Le port d'UKit a trouvé sur iPhone que le signal d'échec de chargement
970
+ > d'une WebView, câblé depuis le jalon 3-E, n'était **jamais lu** : la plateforme tire un événement
971
+ > de fin de chargement pour une navigation en échec, l'agent s'installait sur sa page d'erreur, et la
972
+ > navigation passait pour réussie. Aucune panne réseau de l'Act II n'ouvrait donc de bouton
973
+ > Réessayer. Le correctif tient en deux règles — un événement de chargement n'efface pas un verdict
974
+ > d'échec, et le verdict se lit **avant** la génération — plus deux défauts latents du même chemin
975
+ > (une reprise condamnée par l'échec précédent, un `reload()` sur une vue sans document). Le moteur
976
+ > **Python** avait le même angle mort et le corrige aussi : `navigate` type un échec de transport en
977
+ > `NetworkError`. Trois gardes, dont un cas de conformance partagé, et un double jsdom qui reproduit
978
+ > désormais la séquence de la plateforme — c'est son silence qui avait laissé passer le défaut deux
979
+ > jalons durant. [docs/embedded.md](docs/embedded.md#une-source-injoignable-atteint-unavailable-corrigé-en-053),
980
+ > [docs/acts/continuum.md](docs/acts/continuum.md#une-source-injoignable-est-une-networkerror).
940
981
 
941
982
  ## Sources de référence
942
983
 
@@ -148,6 +148,7 @@ le reste.
148
148
  | `status` | Code de statut (200 par défaut). |
149
149
  | `headers` | En-têtes de réponse (un `Set-Cookie`, un `Location`, …). |
150
150
  | `body` | Corps littéral ; `Content-Type: text/plain` par défaut. |
151
+ | `charset` | Encodage des octets de `body` : `utf-8` (défaut) ou `iso-8859-1`. Une étiquette inconnue **fait échouer le harnais** au lieu de retomber sur UTF-8 — un cas qui servirait silencieusement de l'UTF-8 passerait en ne prouvant rien. |
151
152
  | `json` | Corps sérialisé en JSON compact ; `Content-Type: application/json` par défaut. |
152
153
  | `html` | Corps littéral servi en `text/html` — ce qu'un navigateur exige pour parser un document au lieu d'en montrer la source. |
153
154
  | `echo` | Répond par une description JSON de la requête reçue : `method`, `path`, `query`, `body`, `headers`. |
@@ -156,6 +157,11 @@ le reste.
156
157
  query ou un en-tête, donc ce sont les deux moteurs qui sont comparés — le harnais n'a aucune idée de
157
158
  la façon dont un formulaire devrait être encodé, et c'est bien ainsi.
158
159
 
160
+ `charset`, lui, sert la seule question qu'un corpus en JSON ne peut pas poser autrement : le corps
161
+ qui part sur le réseau n'est plus celui qu'on lit dans le fichier de cas. C'est ce qui permet de
162
+ servir une réponse **mal étiquetée** — des octets UTF-8 annoncés en `iso-8859-1` — et d'exiger des
163
+ deux moteurs le **même** mojibake, plutôt que de faire confiance à chacun pour deviner.
164
+
159
165
  La valeur comparée est un **résumé normalisé** du run. Les identifiants de run et les durées en sont
160
166
  absents : ils diffèrent par nature, et les comparer rendrait chaque cas instable.
161
167
 
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "run-continuum-unreachable-source",
3
+ "kind": "run",
4
+ "requires": "browser",
5
+ "description": "Une source injoignable arrete le run AU step navigate, sur les deux moteurs. Le port 4 est privilegie (rien ne peut s'y lier sans root, et l'OS ne l'attribue jamais en port ephemere) et n'est sur aucune liste de ports bloques : le refus est un VRAI refus de connexion, immediat, sans serveur de fixtures. Un port bloque par politique (1, 7, 9…) donnerait une erreur d'une autre nature — voir docs/embedded.md. Le cas fige la sequence, qui est exactement ce qui divergeait : le moteur embarque declarait la navigation reussie, et l'echec arrivait au step SUIVANT avec le nom que le Blueprint lui avait donne. Une application n'avait alors aucun moyen de distinguer une panne reseau d'une page qui a change.",
6
+ "server": {},
7
+ "blueprint": {
8
+ "aetherius": "1.0",
9
+ "name": "conformance.continuum.unreachable",
10
+ "act": "continuum",
11
+ "options": { "timeout_ms": 8000 },
12
+ "steps": [
13
+ { "id": "nav", "action": "navigate", "url": "http://127.0.0.1:4/" },
14
+ {
15
+ "id": "jamais",
16
+ "action": "wait_for",
17
+ "selector": "#absent",
18
+ "timeout_ms": 500,
19
+ "on_timeout": "fail:PAGE_ABSENTE"
20
+ }
21
+ ],
22
+ "outputs": { "url": "{{ steps.nav.url }}" }
23
+ },
24
+ "expect": {
25
+ "python": {
26
+ "outcome": "rendered",
27
+ "message_contains": ["the source is unreachable", "127.0.0.1:4"],
28
+ "value": {
29
+ "status": "failed",
30
+ "outputs": {},
31
+ "steps": [{ "step_id": "nav", "action": "navigate", "status": "failed" }],
32
+ "events": [
33
+ { "type": "progress", "step_id": null },
34
+ { "type": "step_started", "step_id": "nav" },
35
+ { "type": "error", "step_id": "nav" },
36
+ { "type": "done", "step_id": null }
37
+ ]
38
+ }
39
+ },
40
+ "embedded": {
41
+ "outcome": "rendered",
42
+ "message_contains": ["could not load", "127.0.0.1:4"],
43
+ "value": {
44
+ "status": "failed",
45
+ "outputs": {},
46
+ "steps": [{ "step_id": "nav", "action": "navigate", "status": "failed" }],
47
+ "events": [
48
+ { "type": "progress", "step_id": null },
49
+ { "type": "step_started", "step_id": "nav" },
50
+ { "type": "error", "step_id": "nav" },
51
+ { "type": "done", "step_id": null }
52
+ ]
53
+ }
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,140 @@
1
+ {
2
+ "name": "run-text-body-and-charset",
3
+ "kind": "run",
4
+ "description": "L'extraction from: 'text' rend le corps decode, et le decodage suit l'en-tete de reponse. Le cas qui prouve que les deux moteurs decodent pareil : un corps latin-1 bien etiquete, un corps UTF-8 mal etiquete (le mojibake doit etre le meme des deux cotes, pas 'corrige' par l'un d'eux), un Content-Type sans charset (UTF-8 par defaut) et un corps vide (chaine vide, pas null). Cote embarque, la lecture passe par arrayBuffer plutot que par text().",
5
+ "server": {
6
+ "GET /latin1": {
7
+ "headers": { "Content-Type": "text/csv; charset=ISO-8859-1" },
8
+ "charset": "iso-8859-1",
9
+ "body": "Prénom;Ville\nZoé;Bègles\n"
10
+ },
11
+ "GET /mislabelled": {
12
+ "headers": { "Content-Type": "text/csv; charset=iso-8859-1" },
13
+ "body": "Prénom;Zoé"
14
+ },
15
+ "GET /no-charset": {
16
+ "headers": { "Content-Type": "text/calendar" },
17
+ "body": "BEGIN:VCALENDAR\r\nSUMMARY:Noël\r\nEND:VCALENDAR\r\n"
18
+ },
19
+ "GET /empty": {
20
+ "headers": { "Content-Type": "text/plain" },
21
+ "body": ""
22
+ }
23
+ },
24
+ "blueprint": {
25
+ "aetherius": "1.0",
26
+ "name": "conformance.vector.text",
27
+ "act": "vector",
28
+ "inputs": { "base_url": { "type": "string", "required": true } },
29
+ "steps": [
30
+ {
31
+ "id": "latin1",
32
+ "action": "http.request",
33
+ "url": "{{ inputs.base_url }}/latin1",
34
+ "expect": { "status": 200 },
35
+ "extract": { "raw": { "from": "text" } }
36
+ },
37
+ {
38
+ "id": "mislabelled",
39
+ "action": "http.request",
40
+ "url": "{{ inputs.base_url }}/mislabelled",
41
+ "extract": { "raw": { "from": "text" } }
42
+ },
43
+ {
44
+ "id": "plain",
45
+ "action": "http.request",
46
+ "url": "{{ inputs.base_url }}/no-charset",
47
+ "extract": { "raw": { "from": "text" } }
48
+ },
49
+ {
50
+ "id": "empty",
51
+ "action": "http.request",
52
+ "url": "{{ inputs.base_url }}/empty",
53
+ "extract": { "raw": { "from": "text" } }
54
+ },
55
+ {
56
+ "id": "guard",
57
+ "action": "assert",
58
+ "condition": "{{ 'BEGIN:VCALENDAR' in steps.plain.raw }}",
59
+ "message": "ICAL_SHAPE"
60
+ }
61
+ ],
62
+ "outputs": {
63
+ "latin1": "{{ steps.latin1.raw }}",
64
+ "mislabelled": "{{ steps.mislabelled.raw }}",
65
+ "plain": "{{ steps.plain.raw }}",
66
+ "empty": "{{ steps.empty.raw }}",
67
+ "taille": "{{ steps.plain.raw | length }}"
68
+ }
69
+ },
70
+ "expect": {
71
+ "python": {
72
+ "outcome": "rendered",
73
+ "value": {
74
+ "status": "success",
75
+ "outputs": {
76
+ "latin1": "Prénom;Ville\nZoé;Bègles\n",
77
+ "mislabelled": "Prénom;Zoé",
78
+ "plain": "BEGIN:VCALENDAR\r\nSUMMARY:Noël\r\nEND:VCALENDAR\r\n",
79
+ "empty": "",
80
+ "taille": 46
81
+ },
82
+ "steps": [
83
+ { "step_id": "latin1", "action": "http.request", "status": "success" },
84
+ { "step_id": "mislabelled", "action": "http.request", "status": "success" },
85
+ { "step_id": "plain", "action": "http.request", "status": "success" },
86
+ { "step_id": "empty", "action": "http.request", "status": "success" },
87
+ { "step_id": "guard", "action": "assert", "status": "success" }
88
+ ],
89
+ "events": [
90
+ { "type": "progress", "step_id": null },
91
+ { "type": "step_started", "step_id": "latin1" },
92
+ { "type": "step_finished", "step_id": "latin1" },
93
+ { "type": "step_started", "step_id": "mislabelled" },
94
+ { "type": "step_finished", "step_id": "mislabelled" },
95
+ { "type": "step_started", "step_id": "plain" },
96
+ { "type": "step_finished", "step_id": "plain" },
97
+ { "type": "step_started", "step_id": "empty" },
98
+ { "type": "step_finished", "step_id": "empty" },
99
+ { "type": "step_started", "step_id": "guard" },
100
+ { "type": "step_finished", "step_id": "guard" },
101
+ { "type": "done", "step_id": null }
102
+ ]
103
+ }
104
+ },
105
+ "embedded": {
106
+ "outcome": "rendered",
107
+ "value": {
108
+ "status": "success",
109
+ "outputs": {
110
+ "latin1": "Prénom;Ville\nZoé;Bègles\n",
111
+ "mislabelled": "Prénom;Zoé",
112
+ "plain": "BEGIN:VCALENDAR\r\nSUMMARY:Noël\r\nEND:VCALENDAR\r\n",
113
+ "empty": "",
114
+ "taille": 46
115
+ },
116
+ "steps": [
117
+ { "step_id": "latin1", "action": "http.request", "status": "success" },
118
+ { "step_id": "mislabelled", "action": "http.request", "status": "success" },
119
+ { "step_id": "plain", "action": "http.request", "status": "success" },
120
+ { "step_id": "empty", "action": "http.request", "status": "success" },
121
+ { "step_id": "guard", "action": "assert", "status": "success" }
122
+ ],
123
+ "events": [
124
+ { "type": "progress", "step_id": null },
125
+ { "type": "step_started", "step_id": "latin1" },
126
+ { "type": "step_finished", "step_id": "latin1" },
127
+ { "type": "step_started", "step_id": "mislabelled" },
128
+ { "type": "step_finished", "step_id": "mislabelled" },
129
+ { "type": "step_started", "step_id": "plain" },
130
+ { "type": "step_finished", "step_id": "plain" },
131
+ { "type": "step_started", "step_id": "empty" },
132
+ { "type": "step_finished", "step_id": "empty" },
133
+ { "type": "step_started", "step_id": "guard" },
134
+ { "type": "step_finished", "step_id": "guard" },
135
+ { "type": "done", "step_id": null }
136
+ ]
137
+ }
138
+ }
139
+ }
140
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "text-extract-with-path",
3
+ "description": "from: 'text' rend le corps entier : un 'path' (comme un 'where', un 'selector' ou un 'fields') n'y selectionne rien. Le refuser evite un Blueprint qui croit filtrer et ne filtre pas. C'est une regle du contrat, pas d'un moteur — les deux refusent, a la validation, avant que la requete ne parte.",
4
+ "blueprint": {
5
+ "aetherius": "1.0",
6
+ "name": "conformance.extract.text.path",
7
+ "act": "vector",
8
+ "steps": [
9
+ {
10
+ "id": "cal",
11
+ "action": "http.request",
12
+ "url": "https://example.test/calendar.ics",
13
+ "extract": { "ics": { "from": "text", "path": "$" } }
14
+ }
15
+ ]
16
+ },
17
+ "expect": {
18
+ "python": {
19
+ "outcome": "rejected",
20
+ "error": "BlueprintValidationError",
21
+ "message_contains": ["'ics'", "from='text'", "'path'", "at steps[0].extract.ics"]
22
+ },
23
+ "embedded": {
24
+ "outcome": "rejected",
25
+ "error": "BlueprintValidationError",
26
+ "message_contains": ["'ics'", "from='text'", "'path'", "at steps[0].extract.ics"]
27
+ }
28
+ }
29
+ }
@@ -332,7 +332,7 @@
332
332
  "kind": "object",
333
333
  "required": false,
334
334
  "default": null,
335
- "help": "Named JSONPath/CSS extractions from the response.",
335
+ "help": "Named extractions from the response: from='json' (JSONPath), from='html' (CSS/XPath) or from='text' (the decoded body).",
336
336
  "placeholder": "{\"items\": {\"from\": \"json\", \"path\": \"$[*]\"}}"
337
337
  }
338
338
  ]