procesio-cli 0.1.1__py3-none-any.whl
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.
- procesio_cli/__init__.py +18 -0
- procesio_cli/_cli.py +71 -0
- procesio_cli/agents/__init__.py +0 -0
- procesio_cli/agents/_lib/__init__.py +0 -0
- procesio_cli/agents/_lib/actiondef.py +24 -0
- procesio_cli/agents/_lib/errors.py +40 -0
- procesio_cli/agents/_lib/reversibility.py +236 -0
- procesio_cli/agents/_lib/toolrunner.py +141 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PLAYBOOK.md +62 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PROCESIO-INTEROP.md +76 -0
- procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-TROUBLESHOOTING.md +60 -0
- procesio_cli/agents/connector-builder/README.md +51 -0
- procesio_cli/agents/connector-builder/agent.yaml +61 -0
- procesio_cli/agents/connector-builder/context.py +19 -0
- procesio_cli/agents/connector-builder/handlers/__init__.py +1 -0
- procesio_cli/agents/connector-builder/handlers/checklist.py +71 -0
- procesio_cli/agents/connector-builder/handlers/guidance.py +37 -0
- procesio_cli/agents/connector-builder/handlers/nextstep.py +221 -0
- procesio_cli/agents/connector-builder/handlers/status.py +35 -0
- procesio_cli/agents/connector-builder/knowledge.py +47 -0
- procesio_cli/agents/connector-builder/main.py +93 -0
- procesio_cli/agents/connector-builder/tests/conftest.py +95 -0
- procesio_cli/agents/connector-builder/tests/test_cba_agent.py +148 -0
- procesio_cli/agents/orchestrator/__init__.py +0 -0
- procesio_cli/agents/orchestrator/bootstraplib.py +166 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/00-INDEX.md +60 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/01-PROCESSES.md +230 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/02-FORMS.md +287 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/03-INTEGRATIONS.md +384 -0
- procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/04-DATABASE.md +293 -0
- procesio_cli/agents/procesio/PROCESIO-API-RELIABILITY-DOCTRINE.md +113 -0
- procesio_cli/agents/procesio/PROCESIO-BEST-PRACTICES.md +316 -0
- procesio_cli/agents/procesio/PROCESIO-BUILD-AND-TEST-PLAYBOOK.md +217 -0
- procesio_cli/agents/procesio/PROCESIO-DATASTORE.md +72 -0
- procesio_cli/agents/procesio/PROCESIO-ENVIRONMENTS.md +62 -0
- procesio_cli/agents/procesio/PROCESIO-SCHEDULING.md +47 -0
- procesio_cli/agents/procesio/PROCESIO-VISUAL-ORGANIZATION.md +175 -0
- procesio_cli/agents/procesio/README.md +97 -0
- procesio_cli/agents/procesio/__init__.py +0 -0
- procesio_cli/agents/procesio/agent.yaml +73 -0
- procesio_cli/agents/procesio/audit.py +226 -0
- procesio_cli/agents/procesio/checklist.py +135 -0
- procesio_cli/agents/procesio/context.py +19 -0
- procesio_cli/agents/procesio/handlers/__init__.py +0 -0
- procesio_cli/agents/procesio/handlers/audit.py +39 -0
- procesio_cli/agents/procesio/handlers/checklist.py +34 -0
- procesio_cli/agents/procesio/handlers/guidance.py +36 -0
- procesio_cli/agents/procesio/handlers/status.py +33 -0
- procesio_cli/agents/procesio/handlers/verify.py +39 -0
- procesio_cli/agents/procesio/knowledge.py +115 -0
- procesio_cli/agents/procesio/main.py +89 -0
- procesio_cli/agents/procesio/tests/__init__.py +0 -0
- procesio_cli/agents/procesio/tests/conftest.py +62 -0
- procesio_cli/agents/procesio/tests/test_audit.py +91 -0
- procesio_cli/agents/procesio/tests/test_checklist.py +28 -0
- procesio_cli/agents/procesio/tests/test_guidance.py +78 -0
- procesio_cli/agents/procesio/tests/test_manifest_sync.py +48 -0
- procesio_cli/agents/procesio/tests/test_verify.py +134 -0
- procesio_cli/agents/procesio/tests/test_workspace_scope.py +97 -0
- procesio_cli/agents/procesio/verifylib.py +222 -0
- procesio_cli/dashboard/README.md +117 -0
- procesio_cli/dashboard/__init__.py +18 -0
- procesio_cli/dashboard/serve.py +98 -0
- procesio_cli/dashboard/server/__init__.py +1 -0
- procesio_cli/dashboard/server/config_schemas.py +92 -0
- procesio_cli/dashboard/server/google.py +103 -0
- procesio_cli/dashboard/server/handler.py +160 -0
- procesio_cli/dashboard/server/inventory.py +213 -0
- procesio_cli/dashboard/server/jobs.py +118 -0
- procesio_cli/dashboard/server/llmtest.py +181 -0
- procesio_cli/dashboard/server/procesio_env.py +94 -0
- procesio_cli/dashboard/server/routes.py +74 -0
- procesio_cli/dashboard/server/runner.py +150 -0
- procesio_cli/dashboard/server/security.py +29 -0
- procesio_cli/dashboard/server/setup.py +211 -0
- procesio_cli/dashboard/server/validate.py +152 -0
- procesio_cli/dashboard/tests/conftest.py +16 -0
- procesio_cli/dashboard/tests/test_dashboard.py +225 -0
- procesio_cli/dashboard/tests/test_procesio_env.py +159 -0
- procesio_cli/dashboard/web/actions.js +274 -0
- procesio_cli/dashboard/web/api.js +79 -0
- procesio_cli/dashboard/web/app.js +385 -0
- procesio_cli/dashboard/web/copilot.js +72 -0
- procesio_cli/dashboard/web/google.js +121 -0
- procesio_cli/dashboard/web/index.html +67 -0
- procesio_cli/dashboard/web/procesio_env.js +187 -0
- procesio_cli/dashboard/web/styles.css +207 -0
- procesio_cli/registry.py +370 -0
- procesio_cli/scripts/build-router.py +249 -0
- procesio_cli/scripts/build-tool-skill.py +202 -0
- procesio_cli/scripts/check-requirements.py +131 -0
- procesio_cli/scripts/check-skill-governance.py +174 -0
- procesio_cli/scripts/evaluate-skill-routing.py +217 -0
- procesio_cli/scripts/evaluate-skills.py +316 -0
- procesio_cli/scripts/get-skill.py +122 -0
- procesio_cli/scripts/list-skills.py +68 -0
- procesio_cli/scripts/list-tools.py +73 -0
- procesio_cli/scripts/run-agent.py +59 -0
- procesio_cli/scripts/run-tool.py +123 -0
- procesio_cli/scripts/secret_scan.py +584 -0
- procesio_cli/scripts/set-credential.py +126 -0
- procesio_cli/scripts/validate-skills.py +530 -0
- procesio_cli/skills/PORTFOLIO.md +134 -0
- procesio_cli/skills/__init__.py +6 -0
- procesio_cli/skills/agent-skill-engineer/SKILL.md +226 -0
- procesio_cli/skills/agent-skill-engineer/evals/evals.json +422 -0
- procesio_cli/skills/agent-skill-engineer/references/authoring-standard.md +178 -0
- procesio_cli/skills/agent-skill-engineer/references/decision-framework.md +107 -0
- procesio_cli/skills/agent-skill-engineer/references/domain-evidence-protocol.md +248 -0
- procesio_cli/skills/agent-skill-engineer/references/evaluation-standard.md +191 -0
- procesio_cli/skills/agent-skill-engineer/references/expert-lenses.md +167 -0
- procesio_cli/skills/agent-skill-engineer/references/field-gate-standard.md +231 -0
- procesio_cli/skills/agent-skill-engineer/references/optimization-protocol.md +233 -0
- procesio_cli/skills/agent-skill-engineer/references/optimizer-contract.md +268 -0
- procesio_cli/skills/agent-skill-engineer/references/portability-standard.md +99 -0
- procesio_cli/skills/agent-skill-engineer/references/research-basis.md +128 -0
- procesio_cli/skills/agent-skill-engineer/references/review-standard.md +143 -0
- procesio_cli/skills/agent-skill-engineer/scripts/audit_skill.py +959 -0
- procesio_cli/skills/agent-skill-engineer/scripts/optimize_skill.py +891 -0
- procesio_cli/skills/agent-skill-engineer/scripts/scaffold_skill.py +397 -0
- procesio_cli/skills/evals/baseline-catalog.json +14 -0
- procesio_cli/skills/evals/baseline-routing-v2.json +38 -0
- procesio_cli/skills/evals/baseline.json +37 -0
- procesio_cli/skills/evals/behavioral.json +367 -0
- procesio_cli/skills/evals/field-boundary-development-v1.json +126 -0
- procesio_cli/skills/evals/field-closeout-correction-development-v1.json +23 -0
- procesio_cli/skills/evals/field-closeout-development-v1.json +89 -0
- procesio_cli/skills/evals/portfolio-pressure-training-v1.json +83 -0
- procesio_cli/skills/evals/routing.json +363 -0
- procesio_cli/skills/evals/validation-baseline.json +4 -0
- procesio_cli/skills/procesio-cli/SKILL.md +134 -0
- procesio_cli/skills/procesio-cli/evals/evals.json +111 -0
- procesio_cli/skills/procesio-cli/evals/workflows.json +16 -0
- procesio_cli/skills/procesio-cli/references/connector-lifecycle.md +47 -0
- procesio_cli/skills/procesio-cli/references/credentials-admin.md +113 -0
- procesio_cli/skills/procesio-cli/references/data-verification.md +71 -0
- procesio_cli/skills/procesio-cli/references/documents-files.md +44 -0
- procesio_cli/skills/procesio-cli/references/form-e2e.md +57 -0
- procesio_cli/skills/procesio-cli/references/operation-contract.md +138 -0
- procesio_cli/skills/procesio-cli/references/process-debugging.md +65 -0
- procesio_cli/skills/procesio-cli/references/process-lifecycle.md +88 -0
- procesio_cli/skills/procesio-cli/references/schedules-webhooks.md +58 -0
- procesio_cli/skills/procesio-cli/references/transport-environments.md +53 -0
- procesio_cli/skills/procesio-cli-maintainer/SKILL.md +84 -0
- procesio_cli/skills/procesio-cli-maintainer/evals/evals.json +116 -0
- procesio_cli/skills/procesio-cli-maintainer/references/change-contract.md +34 -0
- procesio_cli/skills/procesio-cli-maintainer/references/mcp-compatibility.md +13 -0
- procesio_cli/skills/procesio-cli-maintainer/references/skill-authoring.md +51 -0
- procesio_cli/skills/procesio-platform-advisor/SKILL.md +82 -0
- procesio_cli/skills/procesio-platform-advisor/evals/evals.json +116 -0
- procesio_cli/skills/procesio-platform-advisor/references/capability-map.md +22 -0
- procesio_cli/skills/procesio-platform-advisor/references/fit-framework.md +35 -0
- procesio_cli/skills/procesio-platform-advisor/references/freshness-policy.md +25 -0
- procesio_cli/skills/procesio-platform-advisor/references/sizing-method.md +70 -0
- procesio_cli/skills/procesio-platform-advisor/scripts/estimate_capacity.py +99 -0
- procesio_cli/skills/procesio-setup/SKILL.md +180 -0
- procesio_cli/skills/sql-server-optimizer/SKILL.md +86 -0
- procesio_cli/skills/sql-server-optimizer/evals/evals.json +116 -0
- procesio_cli/skills/sql-server-optimizer/references/db-context.md +44 -0
- procesio_cli/skills/sql-server-optimizer/references/generic-mode.md +32 -0
- procesio_cli/skills/sql-server-optimizer/references/procesio-mode.md +40 -0
- procesio_cli/skills/sql-server-optimizer/references/sql-rules.md +43 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-indexes.sql +178 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-procs-and-functions.sql +118 -0
- procesio_cli/skills/sql-server-optimizer/scripts/export-tables.sql +367 -0
- procesio_cli/skills/tests/__init__.py +0 -0
- procesio_cli/skills/tests/test_agent_skill_engineer.py +240 -0
- procesio_cli/skills/tests/test_auth_failure_stop.py +25 -0
- procesio_cli/skills/tests/test_capacity_estimator.py +85 -0
- procesio_cli/skills/tests/test_field_boundary_learning.py +100 -0
- procesio_cli/skills/tests/test_field_closeout_learning.py +123 -0
- procesio_cli/skills/tests/test_field_gate_standard.py +76 -0
- procesio_cli/skills/tests/test_operational_playbooks.py +51 -0
- procesio_cli/skills/tests/test_portfolio_pressure_training.py +35 -0
- procesio_cli/skills/tests/test_process_lifecycle_single_run.py +25 -0
- procesio_cli/skills/tests/test_skill_evaluator.py +67 -0
- procesio_cli/skills/tests/test_skill_optimizer.py +473 -0
- procesio_cli/skills/tests/test_skill_portfolio_review.py +70 -0
- procesio_cli/skills/tests/test_skill_release_binding.py +204 -0
- procesio_cli/skills/tests/test_skill_routing.py +74 -0
- procesio_cli/skills/tests/test_skill_scaffold_v2.py +116 -0
- procesio_cli/skills/tests/test_validate_skills.py +115 -0
- procesio_cli/tools/__init__.py +0 -0
- procesio_cli/tools/_lib/README.md +119 -0
- procesio_cli/tools/_lib/__init__.py +0 -0
- procesio_cli/tools/_lib/accounting.py +120 -0
- procesio_cli/tools/_lib/contacts.py +544 -0
- procesio_cli/tools/_lib/creds.py +83 -0
- procesio_cli/tools/_lib/creds_backends.py +578 -0
- procesio_cli/tools/_lib/dbprofiles.py +147 -0
- procesio_cli/tools/_lib/dbquery.py +286 -0
- procesio_cli/tools/_lib/dbschema.py +225 -0
- procesio_cli/tools/_lib/io.py +54 -0
- procesio_cli/tools/_lib/manifest.py +297 -0
- procesio_cli/tools/_lib/skill_release.py +127 -0
- procesio_cli/tools/_lib/skill_resources.py +118 -0
- procesio_cli/tools/_lib/userdata.py +228 -0
- procesio_cli/tools/_lib/webserialize/__init__.py +16 -0
- procesio_cli/tools/_lib/webserialize/admin.py +61 -0
- procesio_cli/tools/_lib/webserialize/client.py +264 -0
- procesio_cli/tools/_lib/webserialize/config.py +96 -0
- procesio_cli/tools/_lib/webserialize/errors.py +27 -0
- procesio_cli/tools/_lib/webserialize/framing.py +45 -0
- procesio_cli/tools/_lib/webserialize/registry.py +47 -0
- procesio_cli/tools/_lib/webserialize/runtime.py +61 -0
- procesio_cli/tools/_lib/webserialize/server.py +510 -0
- procesio_cli/tools/_lib/websession.py +194 -0
- procesio_cli/tools/connector-builder/CONNECTOR-BUILDER-API-NOTES.md +91 -0
- procesio_cli/tools/connector-builder/README.md +101 -0
- procesio_cli/tools/connector-builder/SKILL.md +248 -0
- procesio_cli/tools/connector-builder/__init__.py +7 -0
- procesio_cli/tools/connector-builder/actiondef.py +24 -0
- procesio_cli/tools/connector-builder/client.py +251 -0
- procesio_cli/tools/connector-builder/errors.py +44 -0
- procesio_cli/tools/connector-builder/gen_manifest.py +142 -0
- procesio_cli/tools/connector-builder/handlers/__init__.py +1 -0
- procesio_cli/tools/connector-builder/handlers/admin.py +182 -0
- procesio_cli/tools/connector-builder/handlers/auth.py +74 -0
- procesio_cli/tools/connector-builder/handlers/builds.py +396 -0
- procesio_cli/tools/connector-builder/handlers/common.py +104 -0
- procesio_cli/tools/connector-builder/handlers/files.py +111 -0
- procesio_cli/tools/connector-builder/handlers/raw.py +38 -0
- procesio_cli/tools/connector-builder/handlers/telemetry.py +39 -0
- procesio_cli/tools/connector-builder/main.py +94 -0
- procesio_cli/tools/connector-builder/tests/conftest.py +146 -0
- procesio_cli/tools/connector-builder/tests/test_cb_client.py +86 -0
- procesio_cli/tools/connector-builder/tests/test_cb_dispatch.py +105 -0
- procesio_cli/tools/connector-builder/tests/test_cb_manifest_sync.py +76 -0
- procesio_cli/tools/connector-builder/tests/test_cb_wait.py +123 -0
- procesio_cli/tools/connector-builder/tool.yaml +671 -0
- procesio_cli/tools/framework-map/.gitattributes +2 -0
- procesio_cli/tools/framework-map/README.md +98 -0
- procesio_cli/tools/framework-map/SKILL.md +30 -0
- procesio_cli/tools/framework-map/app.js +188 -0
- procesio_cli/tools/framework-map/fm_builder.py +476 -0
- procesio_cli/tools/framework-map/fm_catalog.py +19 -0
- procesio_cli/tools/framework-map/fm_ro_catalog.py +94 -0
- procesio_cli/tools/framework-map/fm_strings.py +101 -0
- procesio_cli/tools/framework-map/main.py +95 -0
- procesio_cli/tools/framework-map/styles.css +214 -0
- procesio_cli/tools/framework-map/tests/conftest.py +33 -0
- procesio_cli/tools/framework-map/tests/test_fm_build.py +126 -0
- procesio_cli/tools/framework-map/tests/test_fm_manifest_sync.py +63 -0
- procesio_cli/tools/framework-map/tests/test_fm_store_tree.py +75 -0
- procesio_cli/tools/framework-map/tool.yaml +61 -0
- procesio_cli/tools/mysql/MOVED-TO-CENTRAL.md +29 -0
- procesio_cli/tools/mysql/README.md +196 -0
- procesio_cli/tools/mysql/SKILL.md +37 -0
- procesio_cli/tools/mysql/__init__.py +0 -0
- procesio_cli/tools/mysql/connection.py +191 -0
- procesio_cli/tools/mysql/main.py +487 -0
- procesio_cli/tools/mysql/profiles.template.json +1 -0
- procesio_cli/tools/mysql/schema.py +434 -0
- procesio_cli/tools/mysql/tests/__init__.py +0 -0
- procesio_cli/tools/mysql/tests/conftest.py +21 -0
- procesio_cli/tools/mysql/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/mysql/tests/test_mysql.py +201 -0
- procesio_cli/tools/mysql/tests/test_mysql_schema.py +337 -0
- procesio_cli/tools/mysql/tool.yaml +112 -0
- procesio_cli/tools/procesio/DTO-SUBTOOLS-NOTE.md +360 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/00-INDEX.md +67 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/01-ANATOMY.md +209 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/02-CODE-INJECTION.md +309 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/03-DOM-CONTRACT.md +370 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/04-INTERACTION-RECIPES.md +600 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/05-STEPPER-AND-MOTION.md +426 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/06-PROCESS-INTEGRATION.md +347 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/07-DEPLOY-WORKFLOW.md +294 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/08-PITFALLS.md +569 -0
- procesio_cli/tools/procesio/FORM-DEV-GUIDE/09-NAVIGATION-AND-HOSTING.md +308 -0
- procesio_cli/tools/procesio/PHASE4-E2E-NOTES.md +594 -0
- procesio_cli/tools/procesio/PROCESIO-API-CORRECTIONS.md +295 -0
- procesio_cli/tools/procesio/PROCESIO-API-NOTES.md +6976 -0
- procesio_cli/tools/procesio/PROCESIO-AUTH-NOTES.md +226 -0
- procesio_cli/tools/procesio/PROCESIO-CARD-BUILD-NOTES.md +183 -0
- procesio_cli/tools/procesio/PROCESIO-CONNECTORS-NOTES.md +141 -0
- procesio_cli/tools/procesio/PROCESIO-CUSTOM-ACTION-NOTES.md +179 -0
- procesio_cli/tools/procesio/PROCESIO-DOCS-FIX-REPORT.md +123 -0
- procesio_cli/tools/procesio/PROCESIO-ENVIRONMENTS-NOTES.md +94 -0
- procesio_cli/tools/procesio/PROCESIO-FE-VALIDATION-NOTES.md +107 -0
- procesio_cli/tools/procesio/PROCESIO-FOREACH-NOTES.md +199 -0
- procesio_cli/tools/procesio/PROCESIO-FORM-API-HANG-NOTE.md +101 -0
- procesio_cli/tools/procesio/PROCESIO-FORM-SUBMISSION-NOTES.md +172 -0
- procesio_cli/tools/procesio/PROCESIO-HTML-TO-DOCX-NOTES.md +110 -0
- procesio_cli/tools/procesio/PROCESIO-INSTANCE-HISTORY-NOTES.md +242 -0
- procesio_cli/tools/procesio/PROCESIO-METERING-NOTES.md +201 -0
- procesio_cli/tools/procesio/PROCESIO-NODE-CODE-NOTES.md +279 -0
- procesio_cli/tools/procesio/PROCESIO-NODE-MODULE-WHITELIST.md +113 -0
- procesio_cli/tools/procesio/PROCESIO-PYTHON-ACTION-NOTES.md +165 -0
- procesio_cli/tools/procesio/PROCESIO-QUERY-STORE-NOTES.md +826 -0
- procesio_cli/tools/procesio/PROCESIO-RECONCILIATION-PATTERNS.md +104 -0
- procesio_cli/tools/procesio/PROCESIO-RESOURCE-MODEL-NOTES.md +1100 -0
- procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-CRED-OPTIONALITY.md +45 -0
- procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-NOTES.md +252 -0
- procesio_cli/tools/procesio/PROCESIO-SQL-ACTIONS-NOTES.md +284 -0
- procesio_cli/tools/procesio/PROCESIO-USAGE-GUIDE.md +219 -0
- procesio_cli/tools/procesio/README.md +341 -0
- procesio_cli/tools/procesio/SCHEDULE-INPUT-SECURITY-NOTES.md +63 -0
- procesio_cli/tools/procesio/SKILL.md +694 -0
- procesio_cli/tools/procesio/__init__.py +1 -0
- procesio_cli/tools/procesio/actiondef.py +27 -0
- procesio_cli/tools/procesio/assets/html2docx/html2docx.js +2155 -0
- procesio_cli/tools/procesio/auth.py +354 -0
- procesio_cli/tools/procesio/autogen.py +121 -0
- procesio_cli/tools/procesio/client.py +355 -0
- procesio_cli/tools/procesio/config.py +96 -0
- procesio_cli/tools/procesio/data/endpoints.json +2901 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/01-authentication.md +180 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/02-conventions.md +116 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/ANONYMOUS-SURFACE.md +51 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/README.md +104 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/SYMBOL-INDEX.md +444 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/03-auth-users-workspaces.md +731 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/04-processes.md +913 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/05-actions.md +605 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/06-files.md +237 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/07-forms-documents.md +985 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/08-credentials.md +532 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/09-datastore-datatypes.md +830 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/10-custom-urls.md +319 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/11-subscriptions-resources-analytics.md +455 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/12-webhooks-notifications-misc.md +519 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/13-realtime-signalr.md +153 -0
- procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/99-internal-and-other-services.md +52 -0
- procesio_cli/tools/procesio/docs_info/FormBuilder mock.ts +5725 -0
- procesio_cli/tools/procesio/docs_info/Procesio Platform Actions.json +30219 -0
- procesio_cli/tools/procesio/docs_info/form-config-enums.ts +92 -0
- procesio_cli/tools/procesio/docs_info/form-theme.ts +1603 -0
- procesio_cli/tools/procesio/docs_info/process-validation-reference-1.md +2739 -0
- procesio_cli/tools/procesio/dto/__init__.py +13 -0
- procesio_cli/tools/procesio/dto/credential/builder.py +179 -0
- procesio_cli/tools/procesio/dto/credential/config.schema.json +19 -0
- procesio_cli/tools/procesio/dto/credential/description.md +193 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.config.json +11 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.dto.json +28 -0
- procesio_cli/tools/procesio/dto/credential/fixtures/rest.template.json +44 -0
- procesio_cli/tools/procesio/dto/credential/template.dto.json +16 -0
- procesio_cli/tools/procesio/dto/data/_refresh.py +147 -0
- procesio_cli/tools/procesio/dto/data/action_catalog.json +30825 -0
- procesio_cli/tools/procesio/dto/data/action_families.json +55 -0
- procesio_cli/tools/procesio/dto/data/platform_types.json +182 -0
- procesio_cli/tools/procesio/dto/datatype/builder.py +223 -0
- procesio_cli/tools/procesio/dto/datatype/config.schema.json +47 -0
- procesio_cli/tools/procesio/dto/datatype/description.md +85 -0
- procesio_cli/tools/procesio/dto/datatype/fixtures/customer.config.json +41 -0
- procesio_cli/tools/procesio/dto/datatype/fixtures/customer.dto.json +164 -0
- procesio_cli/tools/procesio/dto/datatype/template.dto.json +37 -0
- procesio_cli/tools/procesio/dto/document/builder.py +193 -0
- procesio_cli/tools/procesio/dto/document/config.schema.json +33 -0
- procesio_cli/tools/procesio/dto/document/description.md +124 -0
- procesio_cli/tools/procesio/dto/document/fixtures/invoice.config.json +16 -0
- procesio_cli/tools/procesio/dto/document/fixtures/invoice.dto.json +32 -0
- procesio_cli/tools/procesio/dto/document/template.dto.json +14 -0
- procesio_cli/tools/procesio/dto/form/FORM-STYLING-NOTES.md +142 -0
- procesio_cli/tools/procesio/dto/form/GOLDEN-DRIFT-NOTES.md +72 -0
- procesio_cli/tools/procesio/dto/form/RUN-PROCESS-RESULT-NOTES.md +80 -0
- procesio_cli/tools/procesio/dto/form/addelement.py +277 -0
- procesio_cli/tools/procesio/dto/form/builder.py +1009 -0
- procesio_cli/tools/procesio/dto/form/capture_goldens.py +106 -0
- procesio_cli/tools/procesio/dto/form/code_cipher.py +67 -0
- procesio_cli/tools/procesio/dto/form/config.schema.json +76 -0
- procesio_cli/tools/procesio/dto/form/data_shell.json +1 -0
- procesio_cli/tools/procesio/dto/form/description.md +346 -0
- procesio_cli/tools/procesio/dto/form/elements/approval.json +259 -0
- procesio_cli/tools/procesio/dto/form/elements/assignee.json +137 -0
- procesio_cli/tools/procesio/dto/form/elements/button.json +125 -0
- procesio_cli/tools/procesio/dto/form/elements/chart.json +341 -0
- procesio_cli/tools/procesio/dto/form/elements/chat.json +199 -0
- procesio_cli/tools/procesio/dto/form/elements/checkbox.json +153 -0
- procesio_cli/tools/procesio/dto/form/elements/column.json +77 -0
- procesio_cli/tools/procesio/dto/form/elements/columns.json +78 -0
- procesio_cli/tools/procesio/dto/form/elements/datetime-input.json +304 -0
- procesio_cli/tools/procesio/dto/form/elements/divider.json +54 -0
- procesio_cli/tools/procesio/dto/form/elements/dropdown.json +130 -0
- procesio_cli/tools/procesio/dto/form/elements/dynamic-table-row.json +253 -0
- procesio_cli/tools/procesio/dto/form/elements/file-upload.json +196 -0
- procesio_cli/tools/procesio/dto/form/elements/file-viewer.json +169 -0
- procesio_cli/tools/procesio/dto/form/elements/heading.json +79 -0
- procesio_cli/tools/procesio/dto/form/elements/icon.json +107 -0
- procesio_cli/tools/procesio/dto/form/elements/image.json +174 -0
- procesio_cli/tools/procesio/dto/form/elements/input.json +206 -0
- procesio_cli/tools/procesio/dto/form/elements/list.json +202 -0
- procesio_cli/tools/procesio/dto/form/elements/number-input.json +202 -0
- procesio_cli/tools/procesio/dto/form/elements/paragraph.json +79 -0
- procesio_cli/tools/procesio/dto/form/elements/radiobox.json +174 -0
- procesio_cli/tools/procesio/dto/form/elements/section.json +114 -0
- procesio_cli/tools/procesio/dto/form/elements/select.json +217 -0
- procesio_cli/tools/procesio/dto/form/elements/side-panel.json +146 -0
- procesio_cli/tools/procesio/dto/form/elements/signature-pad.json +141 -0
- procesio_cli/tools/procesio/dto/form/elements/static-table-row.json +84 -0
- procesio_cli/tools/procesio/dto/form/elements/step.json +105 -0
- procesio_cli/tools/procesio/dto/form/elements/stepper.json +126 -0
- procesio_cli/tools/procesio/dto/form/elements/tab.json +77 -0
- procesio_cli/tools/procesio/dto/form/elements/table.json +279 -0
- procesio_cli/tools/procesio/dto/form/elements/tabs.json +114 -0
- procesio_cli/tools/procesio/dto/form/elements/textarea.json +213 -0
- procesio_cli/tools/procesio/dto/form/enum_map.json +68 -0
- procesio_cli/tools/procesio/dto/form/fieldpath.py +284 -0
- procesio_cli/tools/procesio/dto/form/fixtures/reg.config.json +32 -0
- procesio_cli/tools/procesio/dto/form/fixtures/reg.elements.json +638 -0
- procesio_cli/tools/procesio/dto/form/sync_from_mock.py +386 -0
- procesio_cli/tools/procesio/dto/framework.py +125 -0
- procesio_cli/tools/procesio/dto/process/builder.py +1991 -0
- procesio_cli/tools/procesio/dto/process/config.schema.json +159 -0
- procesio_cli/tools/procesio/dto/process/description.md +218 -0
- procesio_cli/tools/procesio/dto/process/fixtures/greet.config.json +32 -0
- procesio_cli/tools/procesio/dto/process/fixtures/greet.dto.json +317 -0
- procesio_cli/tools/procesio/dto/process/naming.py +305 -0
- procesio_cli/tools/procesio/dto/process/normalize.py +74 -0
- procesio_cli/tools/procesio/dto/process/template.dto.json +32 -0
- procesio_cli/tools/procesio/dto/refdata.py +87 -0
- procesio_cli/tools/procesio/dto/registry.py +27 -0
- procesio_cli/tools/procesio/dto/webhook/builder.py +142 -0
- procesio_cli/tools/procesio/dto/webhook/config.schema.json +25 -0
- procesio_cli/tools/procesio/dto/webhook/description.md +92 -0
- procesio_cli/tools/procesio/dto/webhook/fixtures/order.config.json +10 -0
- procesio_cli/tools/procesio/dto/webhook/fixtures/order.dto.json +25 -0
- procesio_cli/tools/procesio/dto/webhook/template.dto.json +14 -0
- procesio_cli/tools/procesio/environments.py +264 -0
- procesio_cli/tools/procesio/errors.py +166 -0
- procesio_cli/tools/procesio/flowmodel/README.md +37 -0
- procesio_cli/tools/procesio/flowmodel/__init__.py +14 -0
- procesio_cli/tools/procesio/flowmodel/digest.py +223 -0
- procesio_cli/tools/procesio/flowmodel/families.py +34 -0
- procesio_cli/tools/procesio/flowmodel/fevalidation.py +1098 -0
- procesio_cli/tools/procesio/flowmodel/formdigest.py +232 -0
- procesio_cli/tools/procesio/flowmodel/inspect.py +78 -0
- procesio_cli/tools/procesio/flowmodel/model.py +82 -0
- procesio_cli/tools/procesio/flowmodel/nodeparam.py +871 -0
- procesio_cli/tools/procesio/flowmodel/reader.py +191 -0
- procesio_cli/tools/procesio/flowmodel/sqlparam.py +410 -0
- procesio_cli/tools/procesio/form_code_keys.py +50 -0
- procesio_cli/tools/procesio/formlint.py +292 -0
- procesio_cli/tools/procesio/gen_manifest.py +118 -0
- procesio_cli/tools/procesio/handlers/__init__.py +1 -0
- procesio_cli/tools/procesio/handlers/auth_actions.py +103 -0
- procesio_cli/tools/procesio/handlers/catalog.py +147 -0
- procesio_cli/tools/procesio/handlers/common.py +90 -0
- procesio_cli/tools/procesio/handlers/customaction_ops.py +146 -0
- procesio_cli/tools/procesio/handlers/datastore_ops.py +482 -0
- procesio_cli/tools/procesio/handlers/datatype_ops.py +275 -0
- procesio_cli/tools/procesio/handlers/dto_actions.py +258 -0
- procesio_cli/tools/procesio/handlers/environment_admin.py +146 -0
- procesio_cli/tools/procesio/handlers/fevalidate.py +236 -0
- procesio_cli/tools/procesio/handlers/files.py +141 -0
- procesio_cli/tools/procesio/handlers/flowgraph.py +229 -0
- procesio_cli/tools/procesio/handlers/flowlint.py +269 -0
- procesio_cli/tools/procesio/handlers/form_chains.py +336 -0
- procesio_cli/tools/procesio/handlers/form_code.py +277 -0
- procesio_cli/tools/procesio/handlers/form_element.py +199 -0
- procesio_cli/tools/procesio/handlers/form_events.py +441 -0
- procesio_cli/tools/procesio/handlers/form_update.py +270 -0
- procesio_cli/tools/procesio/handlers/formprocess.py +211 -0
- procesio_cli/tools/procesio/handlers/generic.py +135 -0
- procesio_cli/tools/procesio/handlers/layout.py +137 -0
- procesio_cli/tools/procesio/handlers/nodeparams.py +1018 -0
- procesio_cli/tools/procesio/handlers/process_layout.py +189 -0
- procesio_cli/tools/procesio/handlers/process_naming.py +213 -0
- procesio_cli/tools/procesio/handlers/processes.py +416 -0
- procesio_cli/tools/procesio/handlers/profile_admin.py +184 -0
- procesio_cli/tools/procesio/handlers/resource_ops.py +447 -0
- procesio_cli/tools/procesio/handlers/schedules.py +320 -0
- procesio_cli/tools/procesio/handlers/sqlactions.py +232 -0
- procesio_cli/tools/procesio/handlers/transport.py +463 -0
- procesio_cli/tools/procesio/handlers/usageguide.py +222 -0
- procesio_cli/tools/procesio/layout/README.md +196 -0
- procesio_cli/tools/procesio/layout/__init__.py +11 -0
- procesio_cli/tools/procesio/layout/adapter.py +736 -0
- procesio_cli/tools/procesio/layout/cycles.py +186 -0
- procesio_cli/tools/procesio/layout/dispatch.py +39 -0
- procesio_cli/tools/procesio/layout/elk/elk_runner.mjs +50 -0
- procesio_cli/tools/procesio/layout/elk/package-lock.json +21 -0
- procesio_cli/tools/procesio/layout/elk/package.json +11 -0
- procesio_cli/tools/procesio/layout/elk_engine.py +568 -0
- procesio_cli/tools/procesio/layout/engine.py +1311 -0
- procesio_cli/tools/procesio/layout/report.py +187 -0
- procesio_cli/tools/procesio/main.py +179 -0
- procesio_cli/tools/procesio/profiles.py +193 -0
- procesio_cli/tools/procesio/reliability.py +193 -0
- procesio_cli/tools/procesio/sqlformat.py +167 -0
- procesio_cli/tools/procesio/swagger.py +51 -0
- procesio_cli/tools/procesio/tests/__init__.py +0 -0
- procesio_cli/tools/procesio/tests/conftest.py +129 -0
- procesio_cli/tools/procesio/tests/test_auth.py +200 -0
- procesio_cli/tools/procesio/tests/test_autogen.py +78 -0
- procesio_cli/tools/procesio/tests/test_builder_attrpath_sqlbind.py +180 -0
- procesio_cli/tools/procesio/tests/test_builder_custom_action_catalog.py +72 -0
- procesio_cli/tools/procesio/tests/test_client.py +114 -0
- procesio_cli/tools/procesio/tests/test_client_reliability.py +139 -0
- procesio_cli/tools/procesio/tests/test_customaction.py +103 -0
- procesio_cli/tools/procesio/tests/test_customaction_test.py +80 -0
- procesio_cli/tools/procesio/tests/test_cycles.py +69 -0
- procesio_cli/tools/procesio/tests/test_datastore.py +378 -0
- procesio_cli/tools/procesio/tests/test_datastore_node.py +94 -0
- procesio_cli/tools/procesio/tests/test_dispatch.py +170 -0
- procesio_cli/tools/procesio/tests/test_document_page_size.py +19 -0
- procesio_cli/tools/procesio/tests/test_dto.py +734 -0
- procesio_cli/tools/procesio/tests/test_edit_dryrun_preview.py +96 -0
- procesio_cli/tools/procesio/tests/test_elk_layout.py +221 -0
- procesio_cli/tools/procesio/tests/test_environments.py +214 -0
- procesio_cli/tools/procesio/tests/test_fevalidate_gate.py +104 -0
- procesio_cli/tools/procesio/tests/test_fevalidation.py +317 -0
- procesio_cli/tools/procesio/tests/test_fieldpath_variables.py +87 -0
- procesio_cli/tools/procesio/tests/test_files.py +72 -0
- procesio_cli/tools/procesio/tests/test_flow_digest.py +202 -0
- procesio_cli/tools/procesio/tests/test_flowlint.py +114 -0
- procesio_cli/tools/procesio/tests/test_flowmodel.py +158 -0
- procesio_cli/tools/procesio/tests/test_form_add_element.py +256 -0
- procesio_cli/tools/procesio/tests/test_form_approval_gaps.py +319 -0
- procesio_cli/tools/procesio/tests/test_form_auto_names.py +60 -0
- procesio_cli/tools/procesio/tests/test_form_chains.py +217 -0
- procesio_cli/tools/procesio/tests/test_form_code_actions.py +188 -0
- procesio_cli/tools/procesio/tests/test_form_code_keys.py +92 -0
- procesio_cli/tools/procesio/tests/test_form_code_style.py +178 -0
- procesio_cli/tools/procesio/tests/test_form_delete_element.py +99 -0
- procesio_cli/tools/procesio/tests/test_form_element_config.py +141 -0
- procesio_cli/tools/procesio/tests/test_form_event_maps.py +135 -0
- procesio_cli/tools/procesio/tests/test_form_event_ops.py +376 -0
- procesio_cli/tools/procesio/tests/test_form_event_replace.py +86 -0
- procesio_cli/tools/procesio/tests/test_form_events.py +92 -0
- procesio_cli/tools/procesio/tests/test_form_parity.py +171 -0
- procesio_cli/tools/procesio/tests/test_form_select_source.py +74 -0
- procesio_cli/tools/procesio/tests/test_form_sync.py +35 -0
- procesio_cli/tools/procesio/tests/test_form_update.py +133 -0
- procesio_cli/tools/procesio/tests/test_formlint.py +155 -0
- procesio_cli/tools/procesio/tests/test_formlint_css_scope.py +38 -0
- procesio_cli/tools/procesio/tests/test_formlint_field_names.py +83 -0
- procesio_cli/tools/procesio/tests/test_guards.py +110 -0
- procesio_cli/tools/procesio/tests/test_hidden_setting_types.py +39 -0
- procesio_cli/tools/procesio/tests/test_html2docx_asset.py +132 -0
- procesio_cli/tools/procesio/tests/test_key_handle_terminology.py +45 -0
- procesio_cli/tools/procesio/tests/test_layout.py +574 -0
- procesio_cli/tools/procesio/tests/test_layout_report.py +88 -0
- procesio_cli/tools/procesio/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/procesio/tests/test_nodedelete.py +151 -0
- procesio_cli/tools/procesio/tests/test_nodeparam.py +826 -0
- procesio_cli/tools/procesio/tests/test_normalize.py +70 -0
- procesio_cli/tools/procesio/tests/test_payload_docs.py +86 -0
- procesio_cli/tools/procesio/tests/test_process_action_naming.py +170 -0
- procesio_cli/tools/procesio/tests/test_process_call_api_seed.py +46 -0
- procesio_cli/tools/procesio/tests/test_process_ext.py +373 -0
- procesio_cli/tools/procesio/tests/test_process_invariants.py +183 -0
- procesio_cli/tools/procesio/tests/test_process_layout.py +117 -0
- procesio_cli/tools/procesio/tests/test_process_naming.py +188 -0
- procesio_cli/tools/procesio/tests/test_process_query_store.py +172 -0
- procesio_cli/tools/procesio/tests/test_processinfo_variable.py +56 -0
- procesio_cli/tools/procesio/tests/test_profiles.py +127 -0
- procesio_cli/tools/procesio/tests/test_put_projects.py +41 -0
- procesio_cli/tools/procesio/tests/test_reliability.py +107 -0
- procesio_cli/tools/procesio/tests/test_removal_gate.py +95 -0
- procesio_cli/tools/procesio/tests/test_resource_ops.py +173 -0
- procesio_cli/tools/procesio/tests/test_save_validity.py +207 -0
- procesio_cli/tools/procesio/tests/test_schedule_cron_shape.py +111 -0
- procesio_cli/tools/procesio/tests/test_schedules.py +309 -0
- procesio_cli/tools/procesio/tests/test_sqlconvert.py +234 -0
- procesio_cli/tools/procesio/tests/test_sqlformat.py +83 -0
- procesio_cli/tools/procesio/tests/test_sqlparam.py +187 -0
- procesio_cli/tools/procesio/tests/test_superseded_actions.py +54 -0
- procesio_cli/tools/procesio/tests/test_surgical_concurrency.py +156 -0
- procesio_cli/tools/procesio/tests/test_tier4.py +123 -0
- procesio_cli/tools/procesio/tests/test_transport.py +147 -0
- procesio_cli/tools/procesio/tests/test_transport_data_stores.py +168 -0
- procesio_cli/tools/procesio/tests/test_transport_mapper_repair.py +150 -0
- procesio_cli/tools/procesio/tests/test_transport_webhook_strip.py +253 -0
- procesio_cli/tools/procesio/tests/test_usage_guide.py +96 -0
- procesio_cli/tools/procesio/tool.yaml +12486 -0
- procesio_cli/tools/sqlserver/MOVED-TO-CENTRAL.md +29 -0
- procesio_cli/tools/sqlserver/README.md +192 -0
- procesio_cli/tools/sqlserver/SKILL.md +37 -0
- procesio_cli/tools/sqlserver/SQLSERVER-SCHEMA-NOTES.md +284 -0
- procesio_cli/tools/sqlserver/__init__.py +0 -0
- procesio_cli/tools/sqlserver/connection.py +242 -0
- procesio_cli/tools/sqlserver/main.py +521 -0
- procesio_cli/tools/sqlserver/profiles.template.json +1 -0
- procesio_cli/tools/sqlserver/schema.py +600 -0
- procesio_cli/tools/sqlserver/tests/__init__.py +0 -0
- procesio_cli/tools/sqlserver/tests/conftest.py +21 -0
- procesio_cli/tools/sqlserver/tests/test_connection.py +75 -0
- procesio_cli/tools/sqlserver/tests/test_dbprofiles.py +92 -0
- procesio_cli/tools/sqlserver/tests/test_dbquery.py +220 -0
- procesio_cli/tools/sqlserver/tests/test_manifest_sync.py +68 -0
- procesio_cli/tools/sqlserver/tests/test_sqlserver.py +326 -0
- procesio_cli/tools/sqlserver/tests/test_sqlserver_schema.py +488 -0
- procesio_cli/tools/sqlserver/tool.yaml +123 -0
- procesio_cli/tools/web/README.md +381 -0
- procesio_cli/tools/web/SKILL.md +37 -0
- procesio_cli/tools/web/WEB-AUTOMATION-NOTES.md +277 -0
- procesio_cli/tools/web/__init__.py +21 -0
- procesio_cli/tools/web/actiondef.py +24 -0
- procesio_cli/tools/web/api.py +444 -0
- procesio_cli/tools/web/brokered.py +107 -0
- procesio_cli/tools/web/driver.py +839 -0
- procesio_cli/tools/web/drivercontract.py +44 -0
- procesio_cli/tools/web/errors.py +54 -0
- procesio_cli/tools/web/handlers/__init__.py +1 -0
- procesio_cli/tools/web/handlers/archive.py +367 -0
- procesio_cli/tools/web/handlers/browse.py +182 -0
- procesio_cli/tools/web/handlers/contacts.py +74 -0
- procesio_cli/tools/web/handlers/sessmgmt.py +120 -0
- procesio_cli/tools/web/main.py +128 -0
- procesio_cli/tools/web/sessions.py +253 -0
- procesio_cli/tools/web/steps.py +162 -0
- procesio_cli/tools/web/tests/__init__.py +0 -0
- procesio_cli/tools/web/tests/conftest.py +197 -0
- procesio_cli/tools/web/tests/test_api.py +330 -0
- procesio_cli/tools/web/tests/test_archive.py +190 -0
- procesio_cli/tools/web/tests/test_brokered.py +138 -0
- procesio_cli/tools/web/tests/test_dispatch.py +132 -0
- procesio_cli/tools/web/tests/test_errors.py +60 -0
- procesio_cli/tools/web/tests/test_manifest_sync.py +76 -0
- procesio_cli/tools/web/tests/test_page_contacts.py +68 -0
- procesio_cli/tools/web/tests/test_sessions.py +145 -0
- procesio_cli/tools/web/tests/test_steps.py +210 -0
- procesio_cli/tools/web/tool.yaml +184 -0
- procesio_cli/tools/xlsx/README.md +34 -0
- procesio_cli/tools/xlsx/SKILL.md +28 -0
- procesio_cli/tools/xlsx/__init__.py +0 -0
- procesio_cli/tools/xlsx/main.py +129 -0
- procesio_cli/tools/xlsx/reader.py +91 -0
- procesio_cli/tools/xlsx/tests/__init__.py +0 -0
- procesio_cli/tools/xlsx/tests/conftest.py +39 -0
- procesio_cli/tools/xlsx/tests/test_xlsx.py +103 -0
- procesio_cli/tools/xlsx/tool.yaml +30 -0
- procesio_cli/webplatform/aat_mcp/AAT-MCP-NOTES.md +244 -0
- procesio_cli/webplatform/aat_mcp/README.md +78 -0
- procesio_cli/webplatform/aat_mcp/bridge.py +705 -0
- procesio_cli/webplatform/aat_mcp/chat_server.py +194 -0
- procesio_cli/webplatform/aat_mcp/chat_surface.snapshot.json +668 -0
- procesio_cli/webplatform/aat_mcp/chat_surface.yaml +372 -0
- procesio_cli/webplatform/aat_mcp/gate.py +68 -0
- procesio_cli/webplatform/aat_mcp/http_server.py +139 -0
- procesio_cli/webplatform/aat_mcp/protocol.py +175 -0
- procesio_cli/webplatform/aat_mcp/server.py +306 -0
- procesio_cli/webplatform/aat_mcp/tests/conftest.py +8 -0
- procesio_cli/webplatform/aat_mcp/tests/test_aat_mcp.py +255 -0
- procesio_cli/webplatform/aat_mcp/tests/test_chat_surface.py +247 -0
- procesio_cli/webplatform/aat_mcp/tests/test_discovery.py +489 -0
- procesio_cli/webplatform/aat_mcp/tests/test_multiuser.py +86 -0
- procesio_cli/webplatform/aat_mcp/tests/test_progressive_discovery.py +89 -0
- procesio_cli/webplatform/aat_mcp/tests/test_protocol.py +180 -0
- procesio_cli/webplatform/aat_mcp/tests/test_skill_discovery.py +23 -0
- procesio_cli-0.1.1.dist-info/METADATA +467 -0
- procesio_cli-0.1.1.dist-info/RECORD +647 -0
- procesio_cli-0.1.1.dist-info/WHEEL +4 -0
- procesio_cli-0.1.1.dist-info/entry_points.txt +7 -0
- procesio_cli-0.1.1.dist-info/licenses/LICENSE +201 -0
- procesio_cli-0.1.1.dist-info/licenses/NOTICE +5 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"""The public Python API other tools import: open_session, run_steps, get_text,
|
|
2
|
+
screenshot, save_session - all with an injected FakeDriver (no real browser)."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from tools.web import api, sessions
|
|
8
|
+
from tools.web.errors import SessionError, UsageError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _seed(name="linkedin"):
|
|
12
|
+
sessions.write_state(name, {"cookies": [{"name": "sid", "value": "v"}], "origins": []})
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_render_pdf_sessionless_writes_pdf(tmp_path, driver_factory):
|
|
16
|
+
out = tmp_path / "invoice.pdf"
|
|
17
|
+
res = api.render_pdf("<h1>hi</h1>", str(out), fmt="Letter",
|
|
18
|
+
driver_factory=driver_factory)
|
|
19
|
+
d = driver_factory.built[0]
|
|
20
|
+
# session-less: no storageState, no user_data_dir, headless
|
|
21
|
+
assert d.init["storage_state"] is None and d.init["user_data_dir"] is None
|
|
22
|
+
assert d.init["headless"] is True
|
|
23
|
+
call = [kw for m, kw in d.calls if m == "render_pdf"][0]
|
|
24
|
+
assert call["html"] == "<h1>hi</h1>" and call["fmt"] == "Letter"
|
|
25
|
+
assert d.closed is True # browser always torn down
|
|
26
|
+
assert res["out"] == str(out) and res["bytes"] > 0
|
|
27
|
+
assert out.read_bytes().startswith(b"%PDF")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_render_pdf_custom_sheet_passes_size_and_margin(tmp_path, driver_factory):
|
|
31
|
+
# A roll-up / poster needs an exact sheet, not a paper name.
|
|
32
|
+
out = tmp_path / "rollup.pdf"
|
|
33
|
+
api.render_pdf("<div>x</div>", str(out), width="1000mm", height="2000mm", margin="0",
|
|
34
|
+
driver_factory=driver_factory)
|
|
35
|
+
call = [kw for m, kw in driver_factory.built[0].calls if m == "render_pdf"][0]
|
|
36
|
+
assert (call["width"], call["height"], call["margin"]) == ("1000mm", "2000mm", "0")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_render_pdf_defaults_keep_paper_format(tmp_path, driver_factory):
|
|
40
|
+
out = tmp_path / "doc.pdf"
|
|
41
|
+
api.render_pdf("<p>x</p>", str(out), driver_factory=driver_factory)
|
|
42
|
+
call = [kw for m, kw in driver_factory.built[0].calls if m == "render_pdf"][0]
|
|
43
|
+
assert call["fmt"] == "A4" and call["width"] is None and call["margin"] is None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_open_session_loads_state_and_starts(isolated_sessions, driver_factory):
|
|
47
|
+
_seed()
|
|
48
|
+
d = api.open_session("linkedin", url="https://x.test", driver_factory=driver_factory)
|
|
49
|
+
assert d.started is True
|
|
50
|
+
# storageState from the saved file was handed to the factory
|
|
51
|
+
assert d.init["storage_state"]["cookies"][0]["value"] == "v"
|
|
52
|
+
# url -> a goto happened
|
|
53
|
+
assert ("goto", {"url": "https://x.test", "timeout": None}) in d.calls
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_open_session_missing_raises(isolated_sessions, driver_factory):
|
|
57
|
+
with pytest.raises(SessionError):
|
|
58
|
+
api.open_session("nope", driver_factory=driver_factory)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_open_session_auto_detects_persistent_profile(isolated_sessions, driver_factory):
|
|
62
|
+
# A `<name>.profile` dir (saved via --persistent) is used WITHOUT any
|
|
63
|
+
# storageState — and without the caller passing user_data_dir.
|
|
64
|
+
prof = sessions.profile_dir("gprof")
|
|
65
|
+
prof.mkdir()
|
|
66
|
+
(prof / "Default").write_text("x", encoding="utf-8") # non-empty
|
|
67
|
+
d = api.open_session("gprof", driver_factory=driver_factory)
|
|
68
|
+
assert d.started is True
|
|
69
|
+
assert d.init["user_data_dir"] == str(prof)
|
|
70
|
+
assert d.init["storage_state"] is None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_run_steps_end_to_end_and_closes(isolated_sessions, driver_factory):
|
|
74
|
+
_seed()
|
|
75
|
+
out = api.run_steps(
|
|
76
|
+
"linkedin",
|
|
77
|
+
[{"do": "extract_text", "selector": "h1", "name": "title"}],
|
|
78
|
+
url="https://x.test",
|
|
79
|
+
driver_factory=driver_factory,
|
|
80
|
+
)
|
|
81
|
+
assert out["session"] == "linkedin"
|
|
82
|
+
assert "title" in out["results"]
|
|
83
|
+
d = driver_factory.built[0]
|
|
84
|
+
assert d.closed is True # always torn down
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_run_steps_headed_flag_threads_through(isolated_sessions, driver_factory):
|
|
88
|
+
_seed()
|
|
89
|
+
api.run_steps("linkedin", [], headless=False, driver_factory=driver_factory)
|
|
90
|
+
assert driver_factory.built[0].init["headless"] is False
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_get_text_with_wait_and_selector(isolated_sessions, driver_factory):
|
|
94
|
+
_seed()
|
|
95
|
+
out = api.get_text("linkedin", "https://x.test", selector="main",
|
|
96
|
+
wait="#ready", driver_factory=driver_factory)
|
|
97
|
+
assert out["session"] == "linkedin"
|
|
98
|
+
assert out["selector"] == "main"
|
|
99
|
+
d = driver_factory.built[0]
|
|
100
|
+
methods = d.methods()
|
|
101
|
+
assert "wait_for" in methods and "extract_text" in methods
|
|
102
|
+
assert d.closed is True
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_screenshot_full_page_default(isolated_sessions, driver_factory):
|
|
106
|
+
_seed()
|
|
107
|
+
out = api.screenshot("linkedin", "https://x.test", "shot.png",
|
|
108
|
+
driver_factory=driver_factory)
|
|
109
|
+
assert out["path"] == "shot.png"
|
|
110
|
+
shot = [kw for m, kw in driver_factory.built[0].calls if m == "screenshot"][0]
|
|
111
|
+
assert shot["full_page"] is True
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_save_session_writes_state_after_signal(isolated_sessions, driver_factory):
|
|
115
|
+
signalled = {"v": False}
|
|
116
|
+
|
|
117
|
+
def wait_for_signal():
|
|
118
|
+
signalled["v"] = True
|
|
119
|
+
|
|
120
|
+
out = api.save_session("newsite", "https://login.test",
|
|
121
|
+
wait_for_signal=wait_for_signal,
|
|
122
|
+
driver_factory=driver_factory)
|
|
123
|
+
assert signalled["v"] is True # human signal was awaited
|
|
124
|
+
assert out["session"] == "newsite"
|
|
125
|
+
# storageState captured from the (headed) driver and persisted
|
|
126
|
+
assert sessions.exists("newsite")
|
|
127
|
+
saved = sessions.load_state("newsite")
|
|
128
|
+
assert saved["cookies"][0]["name"] == "sid"
|
|
129
|
+
# headed browser was requested
|
|
130
|
+
assert driver_factory.built[0].init["headless"] is False
|
|
131
|
+
assert driver_factory.built[0].closed is True
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_save_session_persistent_captures_cookie_sidecar(isolated_sessions, driver_factory):
|
|
135
|
+
"""A persistent save snapshots live cookies (incl. session-scoped) to a
|
|
136
|
+
sidecar, because the on-disk profile won't persist them itself."""
|
|
137
|
+
out = api.save_session("ryv", "https://example.ryver.com",
|
|
138
|
+
wait_for_signal=lambda: None, persistent=True,
|
|
139
|
+
driver_factory=driver_factory)
|
|
140
|
+
assert out["persistent"] is True
|
|
141
|
+
assert out["cookies_saved"] == 1 # FakeDriver returns one cookie
|
|
142
|
+
assert sessions.cookies_exist("ryv")
|
|
143
|
+
saved = sessions.load_cookies("ryv")
|
|
144
|
+
assert saved[0]["name"] == "sid" and saved[0]["expires"] == -1
|
|
145
|
+
# cookies captured AFTER the human signal and BEFORE close
|
|
146
|
+
methods = driver_factory.built[0].methods()
|
|
147
|
+
assert methods.index("get_cookies") < methods.index("close")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_open_session_persistent_reinjects_cookies_before_navigation(isolated_sessions, driver_factory):
|
|
151
|
+
"""Opening a persistent profile that has a cookie sidecar must re-inject the
|
|
152
|
+
cookies into the fresh context BEFORE navigating, so the first request is
|
|
153
|
+
authenticated."""
|
|
154
|
+
prof = sessions.profile_dir("ryv")
|
|
155
|
+
prof.mkdir()
|
|
156
|
+
(prof / "Default").write_text("x", encoding="utf-8")
|
|
157
|
+
sessions.write_cookies("ryv", [{"name": "sid", "value": "z",
|
|
158
|
+
"domain": ".ringhel.ryver.com", "path": "/",
|
|
159
|
+
"expires": -1}])
|
|
160
|
+
d = api.open_session("ryv", url="https://example.ryver.com",
|
|
161
|
+
driver_factory=driver_factory)
|
|
162
|
+
assert d.injected_cookies and d.injected_cookies[0]["value"] == "z"
|
|
163
|
+
methods = d.methods()
|
|
164
|
+
assert methods.index("add_cookies") < methods.index("goto") # inject, THEN navigate
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_open_session_persistent_without_sidecar_skips_injection(isolated_sessions, driver_factory):
|
|
168
|
+
"""No sidecar -> no add_cookies call (back-compat for IndexedDB-auth
|
|
169
|
+
profiles like WhatsApp that never had one)."""
|
|
170
|
+
prof = sessions.profile_dir("wa")
|
|
171
|
+
prof.mkdir()
|
|
172
|
+
(prof / "Default").write_text("x", encoding="utf-8")
|
|
173
|
+
d = api.open_session("wa", url="https://web.whatsapp.com",
|
|
174
|
+
driver_factory=driver_factory)
|
|
175
|
+
assert "add_cookies" not in d.methods()
|
|
176
|
+
assert d.injected_cookies == []
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_save_session_signal_order(isolated_sessions, driver_factory):
|
|
180
|
+
"""storage_state must be captured AFTER the human signal, not before."""
|
|
181
|
+
order = []
|
|
182
|
+
|
|
183
|
+
def wait_for_signal():
|
|
184
|
+
order.append("signal")
|
|
185
|
+
|
|
186
|
+
# wrap the factory to record when storage_state is read
|
|
187
|
+
base = driver_factory
|
|
188
|
+
|
|
189
|
+
def factory(**kw):
|
|
190
|
+
d = base(**kw)
|
|
191
|
+
orig = d.storage_state
|
|
192
|
+
|
|
193
|
+
def traced():
|
|
194
|
+
order.append("capture")
|
|
195
|
+
return orig()
|
|
196
|
+
d.storage_state = traced
|
|
197
|
+
return d
|
|
198
|
+
|
|
199
|
+
api.save_session("ordered", "https://login.test",
|
|
200
|
+
wait_for_signal=wait_for_signal, driver_factory=factory)
|
|
201
|
+
assert order == ["signal", "capture"]
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# --- non-stdin login wait (agent-driven save-session) -----------------------
|
|
205
|
+
|
|
206
|
+
def test_save_session_wait_seconds_detects_login(isolated_sessions, driver_factory,
|
|
207
|
+
monkeypatch):
|
|
208
|
+
"""With --wait-seconds the browser is held open WITHOUT touching stdin, and
|
|
209
|
+
the wait ends as soon as the address bar leaves the login host."""
|
|
210
|
+
monkeypatch.setattr(api, "_POLL_SECONDS", 0)
|
|
211
|
+
monkeypatch.setattr(api, "_SETTLE_SECONDS", 0)
|
|
212
|
+
|
|
213
|
+
def boom(): # stdin must never be consulted
|
|
214
|
+
raise AssertionError("save_session must not block on stdin")
|
|
215
|
+
monkeypatch.setattr(api, "_block_on_enter", boom)
|
|
216
|
+
|
|
217
|
+
out = api.save_session("goog", "https://drive.google.com/drive/my-drive",
|
|
218
|
+
persistent=True, wait_seconds=30,
|
|
219
|
+
wait_until_url_excludes="accounts.google.com",
|
|
220
|
+
driver_factory=driver_factory)
|
|
221
|
+
assert out["waited_for"] == "url"
|
|
222
|
+
assert out["login_detected"] is True # FakeDriver url is off the login host
|
|
223
|
+
assert out["final_url"] == "https://example.com/after" # FakeDriver post-nav url
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_save_session_wait_seconds_reports_timeout(isolated_sessions, monkeypatch):
|
|
227
|
+
"""Still on the login host when time runs out -> login_detected False, so a
|
|
228
|
+
half-authenticated save is never mistaken for success. This is the exact
|
|
229
|
+
case that silently produced a broken `google` session (see
|
|
230
|
+
WEB-AUTOMATION-NOTES.md)."""
|
|
231
|
+
from tools.web.tests.conftest import FakeDriver
|
|
232
|
+
|
|
233
|
+
monkeypatch.setattr(api, "_POLL_SECONDS", 0)
|
|
234
|
+
monkeypatch.setattr(api, "_SETTLE_SECONDS", 0)
|
|
235
|
+
|
|
236
|
+
def stuck_factory(**kwargs):
|
|
237
|
+
# never leaves the login host, however long we wait
|
|
238
|
+
return FakeDriver(url="https://accounts.google.com/signin", **kwargs)
|
|
239
|
+
|
|
240
|
+
out = api.save_session("goog2", "https://accounts.google.com/signin",
|
|
241
|
+
persistent=True, wait_seconds=1,
|
|
242
|
+
wait_until_url_excludes="accounts.google.com",
|
|
243
|
+
driver_factory=stuck_factory)
|
|
244
|
+
assert out["login_detected"] is False
|
|
245
|
+
assert "final_url" not in out
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def test_save_session_rejects_nonpositive_wait(isolated_sessions, driver_factory):
|
|
249
|
+
with pytest.raises(UsageError):
|
|
250
|
+
api.save_session("goog3", "https://example.test", wait_seconds=0,
|
|
251
|
+
driver_factory=driver_factory)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# --- human-handoff hold (agent stages, human commits) ----------------------
|
|
255
|
+
|
|
256
|
+
def _steps():
|
|
257
|
+
return [{"do": "fill", "selector": "#name", "text": "x"}]
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def test_run_hold_ends_early_when_selector_appears(isolated_sessions, driver_factory,
|
|
261
|
+
monkeypatch):
|
|
262
|
+
"""The hold exists so a human can click Submit. FakeDriver.wait_for always
|
|
263
|
+
resolves, i.e. the confirmation element is already there -> reason
|
|
264
|
+
'selector', completed True."""
|
|
265
|
+
monkeypatch.setattr(api, "_POLL_SECONDS", 0)
|
|
266
|
+
|
|
267
|
+
_seed()
|
|
268
|
+
out = api.run_steps("linkedin", _steps(), headless=False, hold_seconds=30,
|
|
269
|
+
hold_until_selector="text=Thank",
|
|
270
|
+
driver_factory=driver_factory)
|
|
271
|
+
assert out["hold"]["completed"] is True
|
|
272
|
+
assert out["hold"]["reason"] == "selector"
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_run_hold_ends_early_when_url_leaves_host(isolated_sessions, driver_factory,
|
|
276
|
+
monkeypatch):
|
|
277
|
+
monkeypatch.setattr(api, "_POLL_SECONDS", 0)
|
|
278
|
+
|
|
279
|
+
_seed()
|
|
280
|
+
out = api.run_steps("linkedin", _steps(), headless=False, hold_seconds=30,
|
|
281
|
+
hold_until_url_excludes="form.example.test",
|
|
282
|
+
driver_factory=driver_factory)
|
|
283
|
+
assert out["hold"]["completed"] is True
|
|
284
|
+
assert out["hold"]["reason"] == "url"
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def test_run_hold_timeout_is_success_not_failure(isolated_sessions, monkeypatch):
|
|
288
|
+
"""The regression this feature exists for: an idle human must NOT turn a
|
|
289
|
+
successful fill into a failed run. No raise, steps' results intact."""
|
|
290
|
+
from tools.web.tests.conftest import FakeDriver
|
|
291
|
+
|
|
292
|
+
monkeypatch.setattr(api, "_POLL_SECONDS", 0)
|
|
293
|
+
|
|
294
|
+
class NeverDriver(FakeDriver):
|
|
295
|
+
def wait_for(self, selector, *, timeout=None):
|
|
296
|
+
raise RuntimeError("selector never appears")
|
|
297
|
+
|
|
298
|
+
_seed()
|
|
299
|
+
out = api.run_steps("linkedin", _steps(), headless=False, hold_seconds=1,
|
|
300
|
+
hold_until_selector="text=Thank",
|
|
301
|
+
driver_factory=lambda **kw: NeverDriver(**kw))
|
|
302
|
+
assert out["hold"]["completed"] is False
|
|
303
|
+
assert out["hold"]["reason"] == "timeout"
|
|
304
|
+
assert out["hold"]["held_seconds"] >= 0
|
|
305
|
+
assert "results" in out # the steps still ran
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def test_run_hold_requires_headed(isolated_sessions, driver_factory):
|
|
309
|
+
with pytest.raises(UsageError):
|
|
310
|
+
api.run_steps("linkedin", _steps(), headless=True, hold_seconds=30,
|
|
311
|
+
driver_factory=driver_factory)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def test_run_hold_rejects_nonpositive(isolated_sessions, driver_factory):
|
|
315
|
+
with pytest.raises(UsageError):
|
|
316
|
+
api.run_steps("linkedin", _steps(), headless=False, hold_seconds=0,
|
|
317
|
+
driver_factory=driver_factory)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def test_run_hold_conditions_require_hold_seconds(isolated_sessions, driver_factory):
|
|
321
|
+
with pytest.raises(UsageError):
|
|
322
|
+
api.run_steps("linkedin", _steps(), headless=False,
|
|
323
|
+
hold_until_selector="text=Thank",
|
|
324
|
+
driver_factory=driver_factory)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def test_run_without_hold_has_no_hold_block(isolated_sessions, driver_factory):
|
|
328
|
+
_seed()
|
|
329
|
+
out = api.run_steps("linkedin", _steps(), driver_factory=driver_factory)
|
|
330
|
+
assert "hold" not in out
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Tests for `web archive-document`.
|
|
2
|
+
|
|
3
|
+
The capture is evidence, so the properties that matter are not "did it write a
|
|
4
|
+
file" but: the original is byte-identical to what the publisher served, the hash
|
|
5
|
+
in the header is the hash of THAT file, the extraction never silently loses the
|
|
6
|
+
document's text, and a date the document states about itself is reported rather
|
|
7
|
+
than reconciled. Each test below pins one of those.
|
|
8
|
+
|
|
9
|
+
No network: the fetch path is exercised through --file, and the stylesheet
|
|
10
|
+
inliner is driven with a stubbed requests module.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import hashlib
|
|
15
|
+
import json
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
|
|
22
|
+
FRAMEWORK_ROOT = Path(__file__).resolve().parents[3]
|
|
23
|
+
if str(FRAMEWORK_ROOT) not in sys.path:
|
|
24
|
+
sys.path.insert(0, str(FRAMEWORK_ROOT))
|
|
25
|
+
|
|
26
|
+
from tools.web.handlers import archive # noqa: E402
|
|
27
|
+
from tools.web.errors import UsageError # noqa: E402
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _Args:
|
|
31
|
+
"""Minimal stand-in for the parsed argparse namespace."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, **kw):
|
|
34
|
+
defaults = dict(url=None, file=None, source_url=None, out_dir=None,
|
|
35
|
+
name=None, title=None, timeout=5, no_self_contained=True)
|
|
36
|
+
defaults.update(kw)
|
|
37
|
+
for k, v in defaults.items():
|
|
38
|
+
setattr(self, k, v)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
HTML = b"""<html><head><title>Terms & Conditions | ACME</title>
|
|
42
|
+
<link rel="stylesheet" href="/s.css"></head>
|
|
43
|
+
<body><h1>Terms</h1><p>Last updated: 10 August 2021</p>
|
|
44
|
+
<p>Client must not resell the Technology.</p>
|
|
45
|
+
<script>var x=1;</script></body></html>"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _run(tmp_path, **kw):
|
|
49
|
+
src = tmp_path / "in.html"
|
|
50
|
+
src.write_bytes(kw.pop("body", HTML))
|
|
51
|
+
return archive.archive_document(
|
|
52
|
+
_Args(file=str(src), out_dir=str(tmp_path / "out"), name="terms", **kw))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_original_is_byte_identical_and_hash_matches_it(tmp_path):
|
|
56
|
+
"""The stored original must equal the input byte for byte, and the reported
|
|
57
|
+
sha256 must be the hash of that file - not of the extraction, and not of a
|
|
58
|
+
re-serialised parse tree. This is the whole basis for comparing a later
|
|
59
|
+
fetch against this one."""
|
|
60
|
+
res = _run(tmp_path)
|
|
61
|
+
original = Path(res["files"]["original"])
|
|
62
|
+
assert original.read_bytes() == HTML
|
|
63
|
+
assert res["sha256"]["original"] == hashlib.sha256(HTML).hexdigest()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_extraction_keeps_document_text_and_drops_only_script(tmp_path):
|
|
67
|
+
"""Dropping a region to tidy the extraction risks dropping a clause that
|
|
68
|
+
happens to sit in it, so only script/style are removed."""
|
|
69
|
+
res = _run(tmp_path)
|
|
70
|
+
text = Path(res["files"]["extraction"]).read_text(encoding="utf-8")
|
|
71
|
+
assert "Client must not resell the Technology." in text
|
|
72
|
+
assert "var x=1" not in text
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_provenance_header_carries_source_and_hash(tmp_path):
|
|
76
|
+
"""An extraction that travels without its source and fetch date becomes an
|
|
77
|
+
unattributable quote the first time someone pastes it into a brief."""
|
|
78
|
+
res = _run(tmp_path, source_url="https://example.com/terms")
|
|
79
|
+
header = Path(res["files"]["extraction"]).read_text(encoding="utf-8")[:1200]
|
|
80
|
+
assert "https://example.com/terms" in header
|
|
81
|
+
assert res["sha256"]["original"] in header
|
|
82
|
+
assert "Fetched at (UTC)" in header
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_title_read_from_the_document_unless_overridden(tmp_path):
|
|
86
|
+
assert _run(tmp_path)["title"] == "Terms & Conditions | ACME"
|
|
87
|
+
assert _run(tmp_path, title="Pinned")["title"] == "Pinned"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_self_contained_copy_is_written_only_when_asked(tmp_path):
|
|
91
|
+
assert "self_contained" not in _run(tmp_path)["files"]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_date_candidates_are_reported_not_reconciled(tmp_path):
|
|
95
|
+
"""Two different dates on one document is a finding about the document. The
|
|
96
|
+
action must surface both rather than pick one."""
|
|
97
|
+
body = (b"<html><body><p>Last updated: 10 August 2021</p>"
|
|
98
|
+
b"<p>Effective 19/12/2023</p></body></html>")
|
|
99
|
+
res = _run(tmp_path, body=body)
|
|
100
|
+
dates = [d["date"] for d in res["document_date_candidates"]]
|
|
101
|
+
assert "10 August 2021" in dates
|
|
102
|
+
assert "19/12/2023" in dates
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_a_bare_date_with_no_version_context_is_not_claimed_as_the_date(tmp_path):
|
|
106
|
+
"""A date mentioned in passing is not the document's own date. Only dates
|
|
107
|
+
near version wording are reported, so the caller is not handed a false
|
|
108
|
+
'this document is dated X'."""
|
|
109
|
+
body = b"<html><body><p>The contract began on 01/01/2020 in Bucharest.</p></body></html>"
|
|
110
|
+
assert _run(tmp_path, body=body)["document_date_candidates"] == []
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_pdf_is_detected_by_magic_bytes_not_extension(tmp_path):
|
|
114
|
+
"""A PDF served without a .pdf path or content-type must still be stored as
|
|
115
|
+
a .pdf and text-extracted as one."""
|
|
116
|
+
src = tmp_path / "doc.bin"
|
|
117
|
+
src.write_bytes(b"%PDF-1.4\n% not a real pdf")
|
|
118
|
+
res = archive.archive_document(
|
|
119
|
+
_Args(file=str(src), out_dir=str(tmp_path / "out"), name="agreement"))
|
|
120
|
+
assert res["kind"] == "pdf"
|
|
121
|
+
assert Path(res["files"]["original"]).suffix == ".pdf"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@pytest.mark.parametrize("kw", [
|
|
125
|
+
dict(), # neither url nor file
|
|
126
|
+
dict(url="https://x/y", file="z.html"), # both
|
|
127
|
+
])
|
|
128
|
+
def test_exactly_one_source_is_required(tmp_path, kw):
|
|
129
|
+
with pytest.raises(UsageError):
|
|
130
|
+
archive.archive_document(
|
|
131
|
+
_Args(out_dir=str(tmp_path), name="n", **kw))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_name_must_not_escape_the_out_dir(tmp_path):
|
|
135
|
+
"""--name becomes a folder; a path separator in it would write outside the
|
|
136
|
+
collection."""
|
|
137
|
+
for bad in ("../evil", "a/b", "a\\b", " "):
|
|
138
|
+
with pytest.raises(UsageError):
|
|
139
|
+
archive.archive_document(
|
|
140
|
+
_Args(file=str(tmp_path), out_dir=str(tmp_path), name=bad))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_stylesheets_are_inlined_with_urls_made_absolute(tmp_path, monkeypatch):
|
|
144
|
+
"""The inlined copy has to render offline, and url() inside a stylesheet is
|
|
145
|
+
relative to the STYLESHEET, not the page - left alone it loses its fonts."""
|
|
146
|
+
class _Resp:
|
|
147
|
+
status_code = 200
|
|
148
|
+
text = "body{background:url('img/bg.png')}"
|
|
149
|
+
|
|
150
|
+
monkeypatch.setattr("requests.get", lambda *a, **k: _Resp())
|
|
151
|
+
soup = archive._soup(HTML)
|
|
152
|
+
assert archive._inline_stylesheets(soup, "https://example.com/terms", 5) == 1
|
|
153
|
+
out = str(soup)
|
|
154
|
+
assert "inlined from https://example.com/s.css" in out
|
|
155
|
+
assert "url('https://example.com/img/bg.png')" in out
|
|
156
|
+
assert "<link" not in out
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_a_failing_stylesheet_degrades_but_never_aborts_the_capture(tmp_path, monkeypatch):
|
|
160
|
+
"""Losing a stylesheet must not cost us the document."""
|
|
161
|
+
def _boom(*a, **k):
|
|
162
|
+
raise RuntimeError("network down")
|
|
163
|
+
|
|
164
|
+
monkeypatch.setattr("requests.get", _boom)
|
|
165
|
+
soup = archive._soup(HTML)
|
|
166
|
+
assert archive._inline_stylesheets(soup, "https://example.com/t", 5) == 0
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_manifest_declares_every_runtime_action(tmp_path):
|
|
170
|
+
"""Manifest is the source of truth (Hard rule 3): an action reachable at
|
|
171
|
+
runtime but absent from tool.yaml is a contract that drifted."""
|
|
172
|
+
import yaml
|
|
173
|
+
from tools.web import main as web_main
|
|
174
|
+
manifest = yaml.safe_load((FRAMEWORK_ROOT / "tools/web/tool.yaml").read_text(encoding="utf-8"))
|
|
175
|
+
declared = {a["name"] for a in manifest["actions"]}
|
|
176
|
+
assert "archive-document" in declared
|
|
177
|
+
assert set(web_main.ACTIONS) == declared
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_cli_emits_one_json_object_on_stdout(tmp_path):
|
|
181
|
+
"""JSON in, JSON out (Hard rule 2)."""
|
|
182
|
+
src = tmp_path / "in.html"
|
|
183
|
+
src.write_bytes(HTML)
|
|
184
|
+
proc = subprocess.run(
|
|
185
|
+
[sys.executable, str(FRAMEWORK_ROOT / "scripts/run-tool.py"), "web",
|
|
186
|
+
"archive-document", "--file", str(src), "--out-dir",
|
|
187
|
+
str(tmp_path / "out"), "--name", "terms"],
|
|
188
|
+
capture_output=True, text=True, cwd=str(FRAMEWORK_ROOT))
|
|
189
|
+
assert proc.returncode == 0, proc.stderr
|
|
190
|
+
assert json.loads(proc.stdout)["name"] == "terms"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""web-tool broker routing tests: generic run/get-text/screenshot against a
|
|
2
|
+
broker-OWNED session are translated to a web-steps op on the owning broker;
|
|
3
|
+
unowned sessions, --direct, --channel and injected factories stay in-process.
|
|
4
|
+
save-session/delete-session stop the owning broker first. No real broker is
|
|
5
|
+
ever spawned (conftest autouse sets all *_DIRECT; tests stub the lib client)."""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from tools._lib.webserialize import client as ws_client
|
|
11
|
+
from tools.web import brokered
|
|
12
|
+
from tools.web.main import dispatch
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _unroute(monkeypatch):
|
|
16
|
+
for env in ("WHATSAPP_DIRECT", "RYVER_WEB_DIRECT",
|
|
17
|
+
"FGO_WEB_DIRECT", "MIRRO_DIRECT"):
|
|
18
|
+
monkeypatch.delenv(env, raising=False)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def captured_web_steps(monkeypatch):
|
|
23
|
+
"""Stub the lib's web_steps; records the submission and returns a canned
|
|
24
|
+
run_steps-shaped result tagged as served by the broker."""
|
|
25
|
+
seen = {}
|
|
26
|
+
|
|
27
|
+
def fake(cfg, session, steps, *, url=None, headless=True,
|
|
28
|
+
read_timeout=None, dispatch_direct=None):
|
|
29
|
+
seen.update(tool=cfg.tool, session=session, steps=steps,
|
|
30
|
+
url=url, headless=headless)
|
|
31
|
+
return {"session": session,
|
|
32
|
+
"results": {"text": "PAGE TEXT"}, "screenshots": ["x.png"],
|
|
33
|
+
"final_url": "https://final/", "diagnostics": {},
|
|
34
|
+
"via": "service", "service_queue_wait_ms": 3}
|
|
35
|
+
|
|
36
|
+
monkeypatch.setattr(ws_client, "web_steps", fake)
|
|
37
|
+
return seen
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_run_routes_to_owning_broker(monkeypatch, captured_web_steps):
|
|
41
|
+
_unroute(monkeypatch)
|
|
42
|
+
out = dispatch("run", ["--session", "ryver", "--url", "https://r/",
|
|
43
|
+
"--actions", '[{"do": "extract_text", "name": "t"}]'])
|
|
44
|
+
assert captured_web_steps["tool"] == "ryver-web"
|
|
45
|
+
assert captured_web_steps["session"] == "ryver"
|
|
46
|
+
assert captured_web_steps["steps"] == [{"do": "extract_text", "name": "t"}]
|
|
47
|
+
assert out["via"] == "service"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_get_text_translation_and_reshape(monkeypatch, captured_web_steps):
|
|
51
|
+
_unroute(monkeypatch)
|
|
52
|
+
out = dispatch("get-text", ["--session", "whatsapp", "--url", "https://w/",
|
|
53
|
+
"--selector", "#main", "--wait", "#pane-side"])
|
|
54
|
+
assert captured_web_steps["steps"] == [
|
|
55
|
+
{"do": "wait_for", "selector": "#pane-side"},
|
|
56
|
+
{"do": "extract_text", "name": "text", "selector": "#main"},
|
|
57
|
+
]
|
|
58
|
+
assert out["text"] == "PAGE TEXT" and out["session"] == "whatsapp"
|
|
59
|
+
assert out["final_url"] == "https://final/" and out["via"] == "service"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_screenshot_translation(monkeypatch, captured_web_steps, tmp_path):
|
|
63
|
+
_unroute(monkeypatch)
|
|
64
|
+
target = str(tmp_path / "shot.png")
|
|
65
|
+
out = dispatch("screenshot", ["--session", "fgo", "--url", "https://f/",
|
|
66
|
+
"--out", target, "--viewport-only"])
|
|
67
|
+
assert captured_web_steps["steps"] == [
|
|
68
|
+
{"do": "screenshot", "path": target, "full_page": False}]
|
|
69
|
+
assert out["path"] == target and out["via"] == "service"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_unowned_session_stays_direct(monkeypatch, driver_factory, isolated_sessions):
|
|
73
|
+
_unroute(monkeypatch)
|
|
74
|
+
monkeypatch.setattr(ws_client, "web_steps",
|
|
75
|
+
lambda *a, **k: pytest.fail("unowned must not route"))
|
|
76
|
+
(isolated_sessions / "google.json").write_text('{"cookies": [], "origins": []}',
|
|
77
|
+
encoding="utf-8")
|
|
78
|
+
out = dispatch("get-text", ["--session", "google", "--url", "https://g/"],
|
|
79
|
+
driver_factory=driver_factory)
|
|
80
|
+
assert "text" in out
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_direct_and_channel_and_env_stay_local(monkeypatch):
|
|
84
|
+
_unroute(monkeypatch)
|
|
85
|
+
from types import SimpleNamespace as NS
|
|
86
|
+
base = dict(direct=False, channel=None, session="whatsapp")
|
|
87
|
+
assert brokered.eligible(NS(**base), driver_factory=None,
|
|
88
|
+
allow_service=True) is not None
|
|
89
|
+
assert brokered.eligible(NS(**{**base, "direct": True}),
|
|
90
|
+
driver_factory=None, allow_service=True) is None
|
|
91
|
+
assert brokered.eligible(NS(**{**base, "channel": "chrome"}),
|
|
92
|
+
driver_factory=None, allow_service=True) is None
|
|
93
|
+
assert brokered.eligible(NS(**base), driver_factory=object(),
|
|
94
|
+
allow_service=True) is None
|
|
95
|
+
assert brokered.eligible(NS(**{**base, "session": "google"}),
|
|
96
|
+
driver_factory=None, allow_service=True) is None
|
|
97
|
+
monkeypatch.setenv("WHATSAPP_DIRECT", "1") # owning tool's kill switch
|
|
98
|
+
assert brokered.eligible(NS(**base), driver_factory=None,
|
|
99
|
+
allow_service=True) is None
|
|
100
|
+
monkeypatch.delenv("WHATSAPP_DIRECT")
|
|
101
|
+
monkeypatch.setenv("WHATSAPP_SERVICE_WORKER", "1") # inside the worker
|
|
102
|
+
assert brokered.eligible(NS(**base), driver_factory=None,
|
|
103
|
+
allow_service=True) is None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_fallback_result_is_not_reshaped(monkeypatch):
|
|
107
|
+
"""When the broker can't start, web_steps falls back to the DIRECT handler
|
|
108
|
+
whose result is already final — route() must return it untouched."""
|
|
109
|
+
_unroute(monkeypatch)
|
|
110
|
+
monkeypatch.setattr(ws_client, "ensure_running", lambda cfg, **k: None)
|
|
111
|
+
from types import SimpleNamespace as NS
|
|
112
|
+
parsed = NS(session="ryver", url="https://r/", selector=None, wait=None,
|
|
113
|
+
headed=False, direct=False, channel=None)
|
|
114
|
+
cfg = brokered.eligible(parsed, driver_factory=None, allow_service=True)
|
|
115
|
+
result = brokered.route(cfg, "get-text", parsed,
|
|
116
|
+
dispatch_direct=lambda: {"text": "DIRECT",
|
|
117
|
+
"session": "ryver"})
|
|
118
|
+
assert result["text"] == "DIRECT" and result["via"] == "direct-fallback"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_save_session_stops_owning_broker(monkeypatch, isolated_sessions):
|
|
122
|
+
from tools.web.handlers import sessmgmt
|
|
123
|
+
stopped = {}
|
|
124
|
+
monkeypatch.setattr(ws_client, "ping", lambda cfg, **k: {"pong": True})
|
|
125
|
+
monkeypatch.setattr(ws_client, "stop",
|
|
126
|
+
lambda cfg, **k: stopped.setdefault("tool", cfg.tool) or True)
|
|
127
|
+
monkeypatch.setattr(sessmgmt.api, "save_session",
|
|
128
|
+
lambda name, url, channel=None, persistent=False, **kw:
|
|
129
|
+
{"session": name, "url": url, "persistent": persistent})
|
|
130
|
+
from types import SimpleNamespace as NS
|
|
131
|
+
out = sessmgmt.save_session(NS(name="whatsapp", url="https://w/",
|
|
132
|
+
channel=None, persistent=True))
|
|
133
|
+
assert stopped["tool"] == "whatsapp-personal"
|
|
134
|
+
assert out["broker_stopped"] == "whatsapp-personal"
|
|
135
|
+
|
|
136
|
+
out = sessmgmt.save_session(NS(name="google", url="https://g/",
|
|
137
|
+
channel=None, persistent=False))
|
|
138
|
+
assert "broker_stopped" not in out
|