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.
- {aetherius-0.5.2 → aetherius-0.5.4}/CHANGELOG.md +144 -1
- {aetherius-0.5.2 → aetherius-0.5.4}/PKG-INFO +48 -7
- {aetherius-0.5.2 → aetherius-0.5.4}/README.md +46 -5
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/README.md +6 -0
- aetherius-0.5.4/conformance/cases/run/17-unreachable-source.json +56 -0
- aetherius-0.5.4/conformance/cases/run/18-text-body-and-charset.json +140 -0
- aetherius-0.5.4/conformance/cases/validation/37-text-extract-with-path.json +29 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/contracts/actions.json +1 -1
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/actions.py +32 -5
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/bridge.py +5 -4
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/driver.py +9 -1
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/data.py +2 -1
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/validator.py +35 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/extraction/dispatch.py +21 -2
- aetherius-0.5.4/src/aetherius/core/extraction/text_extractor.py +81 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/version.py +1 -1
- {aetherius-0.5.2 → aetherius-0.5.4}/.dockerignore +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/.env.example +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/.github/workflows/ci.yml +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/.github/workflows/release.yml +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/.gitignore +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/CONTRIBUTING.md +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/LICENSE +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/Makefile +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/UKIT.king.blueprint.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/direct.linkaetherius.blueprint.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/github.aetherius.blueprint.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/github.github.blueprint.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/github.zeb.blueprint.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/licence.aetherius.blueprint.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/blueprints/record.aetherius.blueprint.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/01-bare-expression-keeps-the-list.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/02-text-around-an-expression-gives-a-string.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/03-python-str-for-bool-and-none.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/04-recursion-into-arrays-and-objects.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/05-date-filters.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/06-filter-binds-tighter-than-not.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/07-filter-binds-tighter-than-comparison.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/08-membership-and-boolean-logic.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/09-inline-conditional-and-is-defined.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/10-undefined-variable-raises.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/11-undefined-path-in-text-raises.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/12-string-that-starts-and-ends-with-an-expression.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/13-several-expressions-with-surrounding-text.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/14-unknown-filter-is-a-limit.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/15-date-outside-iso-is-a-limit.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/16-dict-method-shadows-key.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/expression/17-default-boolean-mode.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/01-jsonpath-list-with-fields.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/02-single-match-is-still-a-list.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/03-fields-with-zero-one-and-many-matches.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/04-quoted-field-name.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/05-where-on-an-absent-field-raises.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/06-where-refuses-a-function-call.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/07-where-refuses-dunder-access.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/08-where-refuses-subscripting.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/09-html-css-with-text-pseudo-element.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/10-html-attributes-and-outer-html.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/11-jsonpath-filter-is-a-limit.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/12-recursive-wildcard-is-a-limit.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/13-where-on-a-nested-field.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/14-where-on-an-absent-nested-field-raises.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/extraction/15-where-boolean-literal-spelling.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/01-vector-request-and-extract.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/02-form-and-query-encoding.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/03-json-body.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/04-when-guard-skips.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/05-flow-if-and-for-each.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/06-expect-status-violated.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/07-json-and-form-refused.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/08-html-extraction.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/09-unattended-confirm.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/10-session-cookie-between-steps.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/11-continuum-navigate-and-extract.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/12-continuum-form-login.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/13-continuum-wait-for-named-failure.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/14-continuum-navigate-status.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/15-continuum-evaluate-and-flow.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/run/16-confirm-timeout-fails.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/truthy/01-truthy-table.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/01-vector-minimal.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/02-parse-malformed-json.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/03-schema-unknown-act.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/04-schema-step-without-action.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/05-model-empty-steps.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/06-wrong-act-click-in-vector.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/07-nested-branch-path.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/08-nested-inherits-step-act.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/10-not-portable-upload.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/11-not-portable-drag.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/12-not-portable-screenshot.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/13-not-portable-notify.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/14-act-oracle-not-embeddable.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/15-step-act-oracle-not-embeddable.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/16-goal-only-phantom.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/17-goal-only-wrong-act.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/18-unknown-action.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/20-example-vector-flow.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/21-example-vector-confirm.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/22-example-continuum-login.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/23-example-continuum-screenshot.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/24-example-vector-notify.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/25-example-oracle-vision.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/26-example-phantom-goal.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/27-example-composition-mixed.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/30-not-portable-xpath-extract.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/31-example-reference-annonces.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/32-example-reference-restaurants.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/33-example-reference-affluence.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/34-example-reference-celcat.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/35-example-reference-sso.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/conformance/cases/validation/36-example-reference-messagerie.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/contracts/blueprint.schema.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/contracts/events.schema.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/contracts/openapi.yaml +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/deploy/.env.example +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/deploy/Dockerfile +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/deploy/aetherius.service +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/deploy/blueprints/.gitkeep +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/deploy/docker-compose.yml +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/docs-ukit/README.md +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/pyproject.toml +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/__main__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/_contracts/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/_contracts/blueprint.schema.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/claude.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/local.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/planning.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_cognition/provider.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_perception.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/_shared.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/browser.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/debug_overlay.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/driver.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/continuum/human_actions.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/driver.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/locator.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/model.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/perception.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/oracle/scan.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/driver.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/loop.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/memory.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/perception.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/phantom/planner.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/auth.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/client.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/acts/vector/impersonate.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/catalog.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/factory.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/templates.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/builder/validation.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/cli/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/cli/_shared.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/cli/approvals.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/cli/schedule.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/config/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/config/secrets.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/config/settings.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/app.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/approvals.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/console.tcss +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/daemon_control.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/run_bridge.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/_pending.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/act_picker.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/io_editor.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/options_editor.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/preview.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/screen.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/builder/step_editor.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/catalog.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/home.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/library.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/library_scan.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/recorder.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/runs.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/_common.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/detail.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/form.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/schedules/screen.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/sessions.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screens/settings.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/screenshots.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/theme.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/confirm.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/event_log.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/form.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/json_preview.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/console/widgets/run_summary.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/base.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/contract.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/flow.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/human.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/interaction.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/navigation.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/notification.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/registry.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/spec.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/actions/vision.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/loader.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/models.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/blueprint/template.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/driver.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/errors.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/events/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/events/bus.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/events/models.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/events/sinks.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/extraction/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/extraction/html_extractor.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/extraction/json_extractor.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/approvals.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/context.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/drivers.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/engine.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/flow.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/healing.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/result.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/selector.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/core/runtime/steps.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/models/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/models/registry.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/models/store/.gitkeep +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/geo.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/identity.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/pool.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/proxy.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/network/transport.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/base.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/_http.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/discord.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/ntfy.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/telegram.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/channels/webhook.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/message.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/registry.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/notify/sink.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/plugins.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_capture_js.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_gesture_js.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_names.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_overlay_js.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_playwright.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_selector_js.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_transform.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/_vector_js.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/base.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/blueprint_recorder.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/capture.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/continuum_backend.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/gesture_recorder.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/selector_synth.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/session.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/recorder/vector_backend.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/app.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/approvals.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/config.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/deps.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/jobs.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/blueprints.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/recorder.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/runs.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/schedules.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/routes/stream.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/alerts.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/manual.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/misfire.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/service.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/scheduler/triggers.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/server/schemas.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/hardening.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/headers.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/patch.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/profile.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/fingerprint/webrtc.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/gestures/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/gestures/data/human_library.json +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/gestures/library.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/gestures/seed.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/input.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/keyboard.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/mouse.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/scroll.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/humanizer/timing.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/ml/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/ml/fingerprint_model.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/ml/motion_model.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/policy.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/session/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/session/store.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/stealth/session/warmup.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/__init__.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/approvals.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/engine.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/models.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/runs.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/schedules.py +0 -0
- {aetherius-0.5.2 → aetherius-0.5.4}/src/aetherius/store/schema.py +0 -0
- {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.
|
|
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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: aetherius
|
|
3
|
-
Version: 0.5.
|
|
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
|
|
134
|
-
|
|
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
|
|
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
|
-
**
|
|
1012
|
-
|
|
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
|
|
61
|
-
|
|
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
|
|
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
|
-
**
|
|
939
|
-
|
|
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/
|
|
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
|
]
|