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,1098 @@
|
|
|
1
|
+
"""Front-end (designer-layer) process validation — a faithful offline port of the
|
|
2
|
+
Process Designer's client-side "Process Errors" checks.
|
|
3
|
+
|
|
4
|
+
Why this exists: POST /api/Projects/validate validates only the RUNTIME layer
|
|
5
|
+
(parameters) and returns EMPTY even when the designer REFUSES to save. The designer's
|
|
6
|
+
"Process Errors" panel is a separate CLIENT-SIDE check of the DESIGNER layer
|
|
7
|
+
(customData) with NO server endpoint. This module replicates that check so a flow can
|
|
8
|
+
be validated offline / before every save, exactly like the FE does before it lets the
|
|
9
|
+
user save.
|
|
10
|
+
|
|
11
|
+
Source of truth: tools/procesio/docs_info/process-validation-reference-1.md — the FE
|
|
12
|
+
team's verbatim dump of Validation.ts + ControlValidators/*. Every check here maps to a
|
|
13
|
+
method/validator there; section refs are noted inline (e.g. "ref §2 noUnconnectedNodes").
|
|
14
|
+
|
|
15
|
+
Two severities:
|
|
16
|
+
* error — deterministic, high-signal, structural/required/placeholder/value-format
|
|
17
|
+
checks + subprocess mapping. These BLOCK a save (the designer would too).
|
|
18
|
+
* warning — the data-type MISMATCH layer, which depends on the FE helper
|
|
19
|
+
getValueDataTypes whose exact source is NOT in the reference. Ported
|
|
20
|
+
best-effort and kept NON-blocking so an imperfect reconstruction can never
|
|
21
|
+
wrongly block a legitimate save. Tunable against real flows.
|
|
22
|
+
|
|
23
|
+
PURE + offline + case-insensitive: exports are PascalCase (Actions, CustomData), the
|
|
24
|
+
live Web-API is camelCase (actions, customData). Every field access goes through `_g`.
|
|
25
|
+
Never mutates the input. No network — subprocess data and the datatype catalog are
|
|
26
|
+
INJECTED (target_vars_of / datatypes) so the whole thing is unit-testable.
|
|
27
|
+
|
|
28
|
+
Born 2026-07-06 from process-validation-reference-1.md.
|
|
29
|
+
"""
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import json
|
|
33
|
+
import re
|
|
34
|
+
from typing import Any, Callable
|
|
35
|
+
|
|
36
|
+
# -- constant reference (ref §6) ----------------------------------------------
|
|
37
|
+
|
|
38
|
+
START_TID = "c0e32108-6e3e-4ab8-96bd-cd61be6edb33"
|
|
39
|
+
STOP_TID = "c0e32108-6e3e-4ab8-96bd-cd61be6edb34"
|
|
40
|
+
FOREACH_TID = "dbef0804-66a9-4f8f-872c-ece1b89b8fdb"
|
|
41
|
+
CALL_SUB_TID = "c37e56fe-d924-4604-a86f-7c93f863fcdf"
|
|
42
|
+
TRIGGER_SUB_TID = "615365f9-9ccb-dd46-85a6-af824b7be897"
|
|
43
|
+
DECISIONAL_TID = "f5dcbb04-253d-4061-99a1-9b2822c2e6d2"
|
|
44
|
+
AI_DECISIONAL_TID = "772aac51-73f5-471d-bf9f-f5099cb30001"
|
|
45
|
+
|
|
46
|
+
DECISIONAL_CASE_SETTING = "11d4044a-8586-47f6-b3ce-1cae5da40f30"
|
|
47
|
+
DELAY_TYPE_SETTING = "f1293589-6ede-4cc6-a04b-cc70e7084cb0"
|
|
48
|
+
|
|
49
|
+
# Data-type GUIDs (ref §6.3)
|
|
50
|
+
BOOLEAN = "0317bfee-b2f5-4bde-bfe8-121212121210"
|
|
51
|
+
INTEGER = "0317bfee-b2f5-4bde-bfe8-121212121211"
|
|
52
|
+
FLOAT = "0317bfee-b2f5-4bde-bfe8-121212121212"
|
|
53
|
+
DOUBLE = "0317bfee-b2f5-4bde-bfe8-121212121213"
|
|
54
|
+
STRING = "0317bfee-b2f5-4bde-bfe8-121212121214"
|
|
55
|
+
DATE = "0317bfee-b2f5-4bde-bfe8-121212121215"
|
|
56
|
+
RELATIONSHIP = "0317bfee-b2f5-4bde-bfe8-121212121216"
|
|
57
|
+
TIME = "0317bfee-b2f5-4bde-bfe8-121212121217"
|
|
58
|
+
DATETIME = "0317bfee-b2f5-4bde-bfe8-121212121218"
|
|
59
|
+
GUID = "0317bfee-b2f5-4bde-bfe8-121212121222"
|
|
60
|
+
NUMBER = "NUMBER"
|
|
61
|
+
FILE = "10c6ac59-3929-49e6-99dc-121212121219"
|
|
62
|
+
JSON_T = "0317bfee-b2f5-4bde-bfe8-121212121220"
|
|
63
|
+
OBJECT = "0317bfee-b2f5-4bde-bfe8-121212121221"
|
|
64
|
+
|
|
65
|
+
_PRIMITIVE_IDS = {BOOLEAN, INTEGER, FLOAT, DOUBLE, STRING, DATE, RELATIONSHIP,
|
|
66
|
+
TIME, DATETIME, GUID, NUMBER}
|
|
67
|
+
# variable-name collision map (ref checkVariables + mapGUIDtoType/typeArray)
|
|
68
|
+
_PRIMITIVE_NAME = {
|
|
69
|
+
BOOLEAN: "boolean", INTEGER: "integer", FLOAT: "float", DOUBLE: "double",
|
|
70
|
+
STRING: "string", DATE: "date", RELATIONSHIP: "relationship", TIME: "time",
|
|
71
|
+
DATETIME: "datetime", GUID: "guid", NUMBER: "number",
|
|
72
|
+
}
|
|
73
|
+
_TYPE_NAMES = set(_PRIMITIVE_NAME.values())
|
|
74
|
+
|
|
75
|
+
# SettingType values (ref §6.4) -> validator key
|
|
76
|
+
AI_DECISIONAL_CASE = "ai-decisional-case"
|
|
77
|
+
_SETTING_TYPE_TO_VALIDATOR = {
|
|
78
|
+
"tabs-payload": "tabs_payload_old",
|
|
79
|
+
"tabs-payload-v2": "tabs_payload",
|
|
80
|
+
"decisional-case": "conditional",
|
|
81
|
+
"ai-decisional-case": "ai_decisional",
|
|
82
|
+
"process-inputs": "process_io",
|
|
83
|
+
"process-outputs": "process_io",
|
|
84
|
+
"column-definition": "column_definition",
|
|
85
|
+
"delay-definition": "delay",
|
|
86
|
+
"map-process-data": "map_process_data",
|
|
87
|
+
"document-mapper": "document_mapper",
|
|
88
|
+
"data-store-mapper": "data_store_mapper",
|
|
89
|
+
"data-store-decisional": "data_store_decisional",
|
|
90
|
+
"map-parameters": "map_parameters",
|
|
91
|
+
# "number" and everything else -> default
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# GUID / dotted-GUID reference regex (ref §2 variableRegex)
|
|
95
|
+
_VAR_RE = re.compile(
|
|
96
|
+
r"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
|
|
97
|
+
r"(\.[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})*)\b(?!\.)")
|
|
98
|
+
# leftover-placeholder regex (ref AbstractControlValidator.hasPlaceholder)
|
|
99
|
+
_PLACEHOLDER_RE = re.compile(
|
|
100
|
+
r"([x]{8}-[x]{4}-[x]{4}-[x]{4}-[a-zA-Z]{11}(\.[x]{8}-[x]{4}-[x]{4}-[x]{4}-[a-zA-Z]{11})*)\b(?!\.)")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# -- case-insensitive access ---------------------------------------------------
|
|
104
|
+
|
|
105
|
+
def _g(obj: Any, *names: str, default=None):
|
|
106
|
+
if not isinstance(obj, dict):
|
|
107
|
+
return default
|
|
108
|
+
low = {str(k).lower(): v for k, v in obj.items()}
|
|
109
|
+
for n in names:
|
|
110
|
+
if n.lower() in low:
|
|
111
|
+
return low[n.lower()]
|
|
112
|
+
return default
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# -- primitive helpers (ref utils/type/*) -------------------------------------
|
|
116
|
+
|
|
117
|
+
def _is_empty(value) -> bool:
|
|
118
|
+
return value is None or value == "" or (isinstance(value, list) and len(value) == 0)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _is_valid_number(x) -> bool:
|
|
122
|
+
if isinstance(x, bool):
|
|
123
|
+
return False
|
|
124
|
+
if isinstance(x, (int, float)):
|
|
125
|
+
return True
|
|
126
|
+
if isinstance(x, str):
|
|
127
|
+
s = x.strip()
|
|
128
|
+
if s == "":
|
|
129
|
+
return False
|
|
130
|
+
try:
|
|
131
|
+
float(s)
|
|
132
|
+
return True
|
|
133
|
+
except ValueError:
|
|
134
|
+
return False
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _is_valid_integer(x) -> bool:
|
|
139
|
+
if isinstance(x, bool):
|
|
140
|
+
return False
|
|
141
|
+
if isinstance(x, int):
|
|
142
|
+
return True
|
|
143
|
+
if isinstance(x, float):
|
|
144
|
+
return x.is_integer()
|
|
145
|
+
if isinstance(x, str):
|
|
146
|
+
s = x.strip()
|
|
147
|
+
if not re.fullmatch(r"[+-]?\d+", s or ""):
|
|
148
|
+
return False
|
|
149
|
+
return True
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _is_boolean(x) -> bool:
|
|
154
|
+
if isinstance(x, bool):
|
|
155
|
+
return True
|
|
156
|
+
if isinstance(x, str):
|
|
157
|
+
return x.strip().lower() in ("true", "false")
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _has_variable(value) -> bool:
|
|
162
|
+
"""ref Variables/Utils.hasVariable — the value string contains a variable ref."""
|
|
163
|
+
if not isinstance(value, str):
|
|
164
|
+
return False
|
|
165
|
+
return bool(_VAR_RE.search(value))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _has_placeholder(value: str) -> bool:
|
|
169
|
+
return bool(_PLACEHOLDER_RE.search(value))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# -- node model helpers --------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
def _template_id(action: dict) -> str | None:
|
|
175
|
+
return _g(action, "ActionTemplateId", "templateId")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _template_name(action: dict) -> str | None:
|
|
179
|
+
return _g(action, "ActionTemplateName", "actionTemplateName", "ActionName", "actionName")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _cd(action: dict) -> dict:
|
|
183
|
+
return _g(action, "CustomData", "customData", default={}) or {}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _shape(action: dict) -> str | None:
|
|
187
|
+
return _g(_cd(action), "type")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _cd_name(action: dict):
|
|
191
|
+
"""The canvas node label = customData.name (the FE model's node.name). Returns a
|
|
192
|
+
sentinel `_ABSENT` when customData has no `name` key at all (older/fixture DTOs that
|
|
193
|
+
keep the name on actionName instead), so callers can fall back correctly."""
|
|
194
|
+
cd = _cd(action)
|
|
195
|
+
if isinstance(cd, dict) and any(str(k).lower() == "name" for k in cd):
|
|
196
|
+
return _g(cd, "name") or ""
|
|
197
|
+
return _ABSENT
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
_ABSENT = object()
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _kind(action: dict) -> str:
|
|
204
|
+
"""Classify an action into a template family, by GUID first then name/shape."""
|
|
205
|
+
tid = _template_id(action)
|
|
206
|
+
if tid == START_TID:
|
|
207
|
+
return "start"
|
|
208
|
+
if tid == STOP_TID:
|
|
209
|
+
return "stop"
|
|
210
|
+
if tid == FOREACH_TID:
|
|
211
|
+
return "foreach"
|
|
212
|
+
if tid == CALL_SUB_TID:
|
|
213
|
+
return "call_sub"
|
|
214
|
+
if tid == TRIGGER_SUB_TID:
|
|
215
|
+
return "trigger_sub"
|
|
216
|
+
if tid == DECISIONAL_TID:
|
|
217
|
+
return "decisional"
|
|
218
|
+
if tid == AI_DECISIONAL_TID:
|
|
219
|
+
return "ai_decisional"
|
|
220
|
+
name = (_template_name(action) or "").strip().lower()
|
|
221
|
+
by_name = {
|
|
222
|
+
"start": "start", "stop": "stop", "for each": "foreach",
|
|
223
|
+
"call subprocess": "call_sub", "trigger subprocess": "trigger_sub",
|
|
224
|
+
"decisional": "decisional", "decision": "decisional",
|
|
225
|
+
"ai decisional": "ai_decisional",
|
|
226
|
+
}
|
|
227
|
+
if name in by_name:
|
|
228
|
+
return by_name[name]
|
|
229
|
+
# legacy DTOs may carry no template id/name — fall back to the canvas label. Only
|
|
230
|
+
# structural templates default their label from the template, so this stays reliable
|
|
231
|
+
# for Start/Stop/For Each/(Trigger) Subprocess; a renamed node just reads as "other".
|
|
232
|
+
cdn = _cd_name(action)
|
|
233
|
+
if cdn is not _ABSENT and (cdn or "").strip().lower() in by_name:
|
|
234
|
+
return by_name[(cdn or "").strip().lower()]
|
|
235
|
+
if _shape(action) == "diamond":
|
|
236
|
+
return "decisional"
|
|
237
|
+
return "other"
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _action_id(action: dict) -> str | None:
|
|
241
|
+
return _g(action, "Id", "id")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _action_name(action: dict) -> str:
|
|
245
|
+
# The FE's node.name IS customData.name; use it when present (even when ""), else fall
|
|
246
|
+
# back to actionName for DTOs that keep the label there (fixtures / some exports).
|
|
247
|
+
cdn = _cd_name(action)
|
|
248
|
+
if cdn is not _ABSENT:
|
|
249
|
+
return cdn
|
|
250
|
+
return _g(action, "ActionName", "actionName", "name", default="") or ""
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _badges(action: dict, suffix: str = "") -> list[str]:
|
|
254
|
+
"""ref AbstractControlValidator.getBadges (offline approximation: no store lookup)."""
|
|
255
|
+
name = _action_name(action)
|
|
256
|
+
tmpl = (_template_name(action) or "").strip()
|
|
257
|
+
badges: list[str] = []
|
|
258
|
+
if name and tmpl and tmpl != name.strip():
|
|
259
|
+
badges.append(name)
|
|
260
|
+
label = (tmpl or name or "action") + suffix
|
|
261
|
+
badges.append(label)
|
|
262
|
+
return badges
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _settings_of(action: dict):
|
|
266
|
+
"""Yield (config_setting) for each top-level setting in the action's configuration."""
|
|
267
|
+
cd = _g(action, "CustomData", default={}) or {}
|
|
268
|
+
for tab in _g(cd, "configuration", default=[]) or []:
|
|
269
|
+
for s in _g(tab, "settings", default=[]) or []:
|
|
270
|
+
if isinstance(s, dict):
|
|
271
|
+
yield s
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _walk_settings_loose(action: dict):
|
|
275
|
+
"""Yield every setting in the configuration, recursing one level into side-panel
|
|
276
|
+
list values (which hold sub-settings that may carry no `id`). Mirrors the reader's
|
|
277
|
+
_walk_settings; used to locate id-less settings like `flow-list`."""
|
|
278
|
+
for s in _settings_of(action):
|
|
279
|
+
yield s
|
|
280
|
+
val = _g(s, "value")
|
|
281
|
+
if isinstance(val, list):
|
|
282
|
+
for sub in val:
|
|
283
|
+
if isinstance(sub, dict) and (_g(sub, "label") or _g(sub, "type")):
|
|
284
|
+
yield sub
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _all_settings_recursive(action: dict) -> list[dict]:
|
|
288
|
+
"""ref AbstractControlValidator.getAllSettings — flatten nested side-panel settings."""
|
|
289
|
+
out: list[dict] = []
|
|
290
|
+
|
|
291
|
+
def walk(s: dict):
|
|
292
|
+
if _g(s, "id") is not None:
|
|
293
|
+
out.append(s)
|
|
294
|
+
val = _g(s, "value")
|
|
295
|
+
if isinstance(val, list):
|
|
296
|
+
for sub in val:
|
|
297
|
+
if isinstance(sub, dict):
|
|
298
|
+
walk(sub)
|
|
299
|
+
for s in _settings_of(action):
|
|
300
|
+
walk(s)
|
|
301
|
+
return out
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# -- ports -> line model (ref §2 lineArray) -----------------------------------
|
|
305
|
+
|
|
306
|
+
def _build_lines(actions: list[dict]) -> dict[str, list[dict]]:
|
|
307
|
+
"""Reconstruct each node's `lineArray` from all actions' Ports (edges live on the
|
|
308
|
+
source action). A line touches a node if it is that node's source OR destination.
|
|
309
|
+
error_path is true for the Error output path (Type==1 or Data.isDefault=='error')."""
|
|
310
|
+
lines: list[dict] = []
|
|
311
|
+
for a in actions:
|
|
312
|
+
src = _action_id(a)
|
|
313
|
+
for p in _g(a, "Ports", "ports", default=[]) or []:
|
|
314
|
+
s = _g(p, "SourceId", "sourceId", default=src)
|
|
315
|
+
d = _g(p, "DestinationId", "destinationId")
|
|
316
|
+
if not s or not d:
|
|
317
|
+
continue
|
|
318
|
+
pdata = _g(p, "Data", "data", default={}) or {}
|
|
319
|
+
is_err = (int(_g(p, "Type", "type", default=0) or 0) == 1) or \
|
|
320
|
+
(_g(pdata, "isDefault") == "error")
|
|
321
|
+
lines.append({"source": s, "dest": d, "error_path": is_err})
|
|
322
|
+
by_node: dict[str, list[dict]] = {}
|
|
323
|
+
for ln in lines:
|
|
324
|
+
by_node.setdefault(ln["source"], []).append(ln)
|
|
325
|
+
if ln["dest"] != ln["source"]:
|
|
326
|
+
by_node.setdefault(ln["dest"], []).append(ln)
|
|
327
|
+
return by_node
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
# =============================================================================
|
|
331
|
+
# Warning helper
|
|
332
|
+
# =============================================================================
|
|
333
|
+
|
|
334
|
+
def _w(severity: str, code: str, text: str, action: dict | None = None,
|
|
335
|
+
suffix: str = "", badges: list[str] | None = None) -> dict:
|
|
336
|
+
return {
|
|
337
|
+
"severity": severity,
|
|
338
|
+
"code": code,
|
|
339
|
+
"text": text,
|
|
340
|
+
"badges": badges if badges is not None else (_badges(action, suffix) if action else []),
|
|
341
|
+
"actionId": _action_id(action) if action else None,
|
|
342
|
+
"action": _action_name(action) if action else None,
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
# =============================================================================
|
|
347
|
+
# getValueDataTypes + validateDataType (the WARNING type layer, ref §3 / glossary)
|
|
348
|
+
# =============================================================================
|
|
349
|
+
|
|
350
|
+
def _resolve_ref_type(ref: str, var_by_id: dict, dt_by_id: dict) -> tuple[str | None, bool] | None:
|
|
351
|
+
"""Resolve a dotted variable reference (v.attr.attr) to (dataTypeId, isList).
|
|
352
|
+
Returns None if the root variable is unknown."""
|
|
353
|
+
parts = ref.split(".")
|
|
354
|
+
root = var_by_id.get(parts[0])
|
|
355
|
+
if not root:
|
|
356
|
+
return None
|
|
357
|
+
dt = _g(root, "DataType", "dataType")
|
|
358
|
+
is_list = bool(_g(root, "IsList", "isList", default=False))
|
|
359
|
+
for attr_id in parts[1:]:
|
|
360
|
+
model = dt_by_id.get(dt)
|
|
361
|
+
if not model:
|
|
362
|
+
return (None, is_list)
|
|
363
|
+
attr = next((at for at in (_g(model, "attributes", "Attributes", default=[]) or [])
|
|
364
|
+
if _g(at, "id", "Id") == attr_id), None)
|
|
365
|
+
if not attr:
|
|
366
|
+
return (None, is_list)
|
|
367
|
+
dt = _g(attr, "dataTypeId", "DataTypeId", "dataType", "DataType")
|
|
368
|
+
is_list = bool(_g(attr, "isList", "IsList", default=False))
|
|
369
|
+
return (dt, is_list)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def get_value_data_types(value, var_by_id: dict, dt_by_id: dict) -> dict:
|
|
373
|
+
"""ref Values/ValueDataTypesHelper.getValueDataTypes — parse variable refs in a raw
|
|
374
|
+
value and return the datatypes they resolve to, split scalar vs list.
|
|
375
|
+
A plain constant (no variable refs) yields empty lists ({} => "valid, skip")."""
|
|
376
|
+
scalar: list[dict] = []
|
|
377
|
+
listed: list[dict] = []
|
|
378
|
+
if not isinstance(value, str):
|
|
379
|
+
return {"scalar": scalar, "list": listed}
|
|
380
|
+
for m in _VAR_RE.finditer(value):
|
|
381
|
+
resolved = _resolve_ref_type(m.group(1), var_by_id, dt_by_id)
|
|
382
|
+
if resolved is None:
|
|
383
|
+
continue
|
|
384
|
+
dt_id, is_list = resolved
|
|
385
|
+
if dt_id is None:
|
|
386
|
+
continue
|
|
387
|
+
model = dt_by_id.get(dt_id) or {"id": dt_id}
|
|
388
|
+
(listed if is_list else scalar).append(model)
|
|
389
|
+
return {"scalar": scalar, "list": listed}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _is_primitive(dt_id: str) -> bool:
|
|
393
|
+
return dt_id in _PRIMITIVE_IDS
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _is_custom_type_allowed(dt_id: str) -> bool:
|
|
397
|
+
return dt_id in (JSON_T, OBJECT)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def validate_data_type(types: dict, setting: dict, excluded: list[str] | None = None) -> bool:
|
|
401
|
+
"""ref §3 SettingValidation.validateDataType — true if the value's actual types are
|
|
402
|
+
assignable to what the setting expects (dataTypeId / isList)."""
|
|
403
|
+
excluded = excluded or []
|
|
404
|
+
scalar = types.get("scalar", [])
|
|
405
|
+
listed = types.get("list", [])
|
|
406
|
+
if len(scalar) == 0 and len(listed) == 0:
|
|
407
|
+
return True
|
|
408
|
+
is_list = bool(_g(setting, "isList", "IsList", default=False))
|
|
409
|
+
if (is_list and (len(scalar) != 0 or len(listed) != 1)) or \
|
|
410
|
+
(not is_list and (len(scalar) == 0 or len(listed) != 0)):
|
|
411
|
+
return False
|
|
412
|
+
setting_dt = _g(setting, "dataTypeId", "DataTypeId")
|
|
413
|
+
for dt in [*scalar, *listed]:
|
|
414
|
+
if not dt:
|
|
415
|
+
continue
|
|
416
|
+
dt_id = _g(dt, "id", "Id")
|
|
417
|
+
if dt_id in excluded:
|
|
418
|
+
flag = False
|
|
419
|
+
elif setting_dt == OBJECT:
|
|
420
|
+
flag = True
|
|
421
|
+
elif _is_primitive(dt_id):
|
|
422
|
+
flag = True if setting_dt == STRING else (dt_id == setting_dt)
|
|
423
|
+
else:
|
|
424
|
+
flag = _is_custom_type_allowed(setting_dt) or (dt_id == setting_dt)
|
|
425
|
+
if not flag:
|
|
426
|
+
return False
|
|
427
|
+
return True
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
# =============================================================================
|
|
431
|
+
# Field-level validators (ref §4) — dispatched per setting.type
|
|
432
|
+
# =============================================================================
|
|
433
|
+
|
|
434
|
+
def _placeholders_check(action: dict, setting: dict) -> list[dict]:
|
|
435
|
+
"""ref AbstractControlValidator.doPlaceholdersCheck — runs for EVERY setting."""
|
|
436
|
+
value = _g(setting, "value")
|
|
437
|
+
if not value:
|
|
438
|
+
return []
|
|
439
|
+
if isinstance(value, (dict, list)):
|
|
440
|
+
value = json.dumps(value)
|
|
441
|
+
if isinstance(value, str) and (_has_placeholder(value) or "<%%>" in value):
|
|
442
|
+
return [_w("error", "PLACEHOLDER",
|
|
443
|
+
"Please replace all placeholders in the template.",
|
|
444
|
+
action, f" - {_g(setting, 'label', default='')}")]
|
|
445
|
+
return []
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _label(setting: dict) -> str:
|
|
449
|
+
return _g(setting, "label", "Label", default="") or ""
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def _default_required(action, setting) -> list[dict]:
|
|
453
|
+
if _g(setting, "isRequired", "IsRequired") and _is_empty(_g(setting, "value")):
|
|
454
|
+
return [_w("error", "REQUIRED",
|
|
455
|
+
"Please make sure that the action is defined/configured properly.",
|
|
456
|
+
action, f" - {_label(setting)}")]
|
|
457
|
+
return []
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _default_value_check(action, setting) -> list[dict]:
|
|
461
|
+
"""ref DefaultControlValidator.doValueCheck — number/integer literal well-formedness."""
|
|
462
|
+
value = _g(setting, "value")
|
|
463
|
+
if value is None or _has_variable(value):
|
|
464
|
+
return []
|
|
465
|
+
is_list = bool(_g(setting, "isList", "IsList", default=False))
|
|
466
|
+
values = value if (is_list and isinstance(value, list)) else [value]
|
|
467
|
+
if not isinstance(values, list):
|
|
468
|
+
# try to parse a JSON list literal
|
|
469
|
+
try:
|
|
470
|
+
parsed = json.loads(values.replace("'", '"')) if isinstance(values, str) else values
|
|
471
|
+
if isinstance(parsed, list):
|
|
472
|
+
values = parsed
|
|
473
|
+
else:
|
|
474
|
+
values = [value]
|
|
475
|
+
except Exception: # noqa: BLE001
|
|
476
|
+
values = [value]
|
|
477
|
+
# An empty / unset value is NOT a format error — the required check owns emptiness.
|
|
478
|
+
# (ref: the FE's isValidNumber/isValidInteger treat "" as valid, so an optional numeric
|
|
479
|
+
# field left blank — e.g. a Delay's unused "Runtime Amount" — must not be flagged.)
|
|
480
|
+
values = [v for v in values if not (v is None or (isinstance(v, str) and v.strip() == ""))]
|
|
481
|
+
if not values:
|
|
482
|
+
return []
|
|
483
|
+
dt = _g(setting, "dataTypeId", "DataTypeId")
|
|
484
|
+
stype = _g(setting, "type")
|
|
485
|
+
if dt in (FLOAT, DOUBLE, NUMBER):
|
|
486
|
+
if not all(_is_valid_number(v) for v in values):
|
|
487
|
+
return [_w("error", "VALUE_NUMBER", "Please make sure that the value is a number.",
|
|
488
|
+
action, f" - {_label(setting)}")]
|
|
489
|
+
elif dt == INTEGER:
|
|
490
|
+
if not all(_is_valid_integer(v) for v in values):
|
|
491
|
+
return [_w("error", "VALUE_INTEGER", "Please make sure that the value is an integer.",
|
|
492
|
+
action, f" - {_label(setting)}")]
|
|
493
|
+
elif dt == OBJECT and stype == "number":
|
|
494
|
+
if not all(_is_valid_number(v) for v in values):
|
|
495
|
+
return [_w("error", "VALUE_NUMBER", "Please make sure that the value is a number.",
|
|
496
|
+
action, f" - {_label(setting)}")]
|
|
497
|
+
return []
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def _dt_label(dt_by_id: dict, dt_id, is_list: bool = False) -> str:
|
|
501
|
+
name = _g(dt_by_id.get(dt_id) or {}, "name", "Name") or (dt_id or "?")
|
|
502
|
+
return f"list<{name}>" if is_list else str(name)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _type_hint(setting, types: dict, dt_by_id: dict) -> str:
|
|
506
|
+
"""Name the expected vs actual type. "Error: data type mismatch." alone does not
|
|
507
|
+
say WHAT to change; a real case (2026-07-30) was Call API's `Response Status`,
|
|
508
|
+
which needs an **integer** variable — binding a `number` failed with only that
|
|
509
|
+
opaque text, once as an FE warning and again as a BE error."""
|
|
510
|
+
expected = _dt_label(dt_by_id, _g(setting, "dataTypeId", "DataTypeId"),
|
|
511
|
+
bool(_g(setting, "isList", "IsList", default=False)))
|
|
512
|
+
actual = [_dt_label(dt_by_id, _g(dt, "id", "Id")) for dt in (types.get("scalar") or [])]
|
|
513
|
+
actual += [_dt_label(dt_by_id, _g(dt, "id", "Id"), True) for dt in (types.get("list") or [])]
|
|
514
|
+
got = ", ".join(a for a in actual if a) or "nothing"
|
|
515
|
+
return (f"binds {got}; this property expects {expected}. Retype the variable (or "
|
|
516
|
+
f"rebind) to match — the BE validator rejects the same mismatch on save.")
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def _default_datatype_check(action, setting, var_by_id, dt_by_id) -> list[dict]:
|
|
520
|
+
"""ref DefaultControlValidator.doDataTypeCheck — WARNING layer."""
|
|
521
|
+
value = _g(setting, "value") or ""
|
|
522
|
+
if not isinstance(value, str):
|
|
523
|
+
return []
|
|
524
|
+
types = get_value_data_types(value, var_by_id, dt_by_id)
|
|
525
|
+
if not validate_data_type(types, setting):
|
|
526
|
+
w = _w("warning", "TYPE_MISMATCH", "Error: data type mismatch.",
|
|
527
|
+
action, f" - {_label(setting)}")
|
|
528
|
+
w["hint"] = _type_hint(setting, types, dt_by_id) # additive; text kept for FE parity
|
|
529
|
+
return [w]
|
|
530
|
+
return []
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def _ai_decisional_required(action, setting) -> list[dict]:
|
|
534
|
+
"""ref §4.4 AiDecisionalCaseValidator."""
|
|
535
|
+
value = _g(setting, "value")
|
|
536
|
+
if not isinstance(value, list) or len(value) == 0:
|
|
537
|
+
return []
|
|
538
|
+
incomplete = any(
|
|
539
|
+
not (_g(c, "name") or "").strip() or not (_g(c, "condition") or "").strip()
|
|
540
|
+
or not _g(c, "target")
|
|
541
|
+
for c in value if isinstance(c, dict))
|
|
542
|
+
if incomplete:
|
|
543
|
+
return [_w("error", "AI_CASE_INCOMPLETE",
|
|
544
|
+
"Action definition incomplete. Please make sure all AI cases have a "
|
|
545
|
+
"name, condition, and target configured.", action, f" - {_label(setting)}")]
|
|
546
|
+
return []
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def _tabs_payload_required(action, setting) -> list[dict]:
|
|
550
|
+
"""ref §4.5 TabsPayloadControlValidator (v2): row has value but no key."""
|
|
551
|
+
value = _g(setting, "value") or {}
|
|
552
|
+
errors: list[dict] = []
|
|
553
|
+
body = _g(value, "body", default={}) or {}
|
|
554
|
+
btype = _g(body, "type")
|
|
555
|
+
bval = _g(body, "value", default={}) or {}
|
|
556
|
+
active = _g(bval, btype) if btype else None
|
|
557
|
+
label = _label(setting)
|
|
558
|
+
|
|
559
|
+
def row_missing_key(item):
|
|
560
|
+
return _g(item, "value") and len(str(_g(item, "value"))) > 0 and \
|
|
561
|
+
(not _g(item, "key") or len(str(_g(item, "key")).strip()) == 0)
|
|
562
|
+
|
|
563
|
+
if isinstance(active, list):
|
|
564
|
+
for i, item in enumerate(active):
|
|
565
|
+
if isinstance(item, dict) and row_missing_key(item):
|
|
566
|
+
errors.append(_w("error", "PAYLOAD_ROW_NO_KEY",
|
|
567
|
+
"Please make sure that the action is defined/configured properly.",
|
|
568
|
+
action, f" - {label} (Body, row {i + 1})"))
|
|
569
|
+
for key, subname in (("queryParams", "Query Params"), ("headers", "Headers")):
|
|
570
|
+
for i, item in enumerate(_g(value, key, default=[]) or []):
|
|
571
|
+
if isinstance(item, dict) and row_missing_key(item):
|
|
572
|
+
errors.append(_w("error", "PAYLOAD_ROW_NO_KEY",
|
|
573
|
+
"Please make sure that the action is defined/configured properly.",
|
|
574
|
+
action, f" - {label} ({subname}, row {i + 1})"))
|
|
575
|
+
return errors
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _tabs_payload_old_required(action, setting) -> list[dict]:
|
|
579
|
+
"""ref §4.6 TabsPayloadOldControlValidator: query/header row value w/o key."""
|
|
580
|
+
value = _g(setting, "value") or {}
|
|
581
|
+
errors: list[dict] = []
|
|
582
|
+
label = _label(setting)
|
|
583
|
+
for key, subname in (("queryParams", "Query Params"), ("headers", "Headers")):
|
|
584
|
+
for item in _g(value, key, default=[]) or []:
|
|
585
|
+
if isinstance(item, dict) and _g(item, "value") and \
|
|
586
|
+
len(str(_g(item, "value"))) > 0 and \
|
|
587
|
+
(not _g(item, "key") or len(str(_g(item, "key")).strip()) == 0):
|
|
588
|
+
errors.append(_w("error", "PAYLOAD_ROW_NO_KEY",
|
|
589
|
+
"Please make sure that the action is defined/configured properly.",
|
|
590
|
+
action, f" - {label} ({subname})"))
|
|
591
|
+
return errors
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def _process_io_required(action, setting) -> list[dict]:
|
|
595
|
+
"""ref §4.7 ProcessInputOutputValidator."""
|
|
596
|
+
value = _g(setting, "value")
|
|
597
|
+
label = _label(setting)
|
|
598
|
+
if value is None:
|
|
599
|
+
return [_w("error", "REQUIRED",
|
|
600
|
+
"Please make sure that the action is defined/configured properly.",
|
|
601
|
+
action, f" - {label}")]
|
|
602
|
+
errors: list[dict] = []
|
|
603
|
+
if isinstance(value, list):
|
|
604
|
+
if len(value) == 0:
|
|
605
|
+
return []
|
|
606
|
+
for i, row in enumerate(value):
|
|
607
|
+
sub = _g(row, "subprocess")
|
|
608
|
+
if not sub or len(str(sub).strip()) == 0:
|
|
609
|
+
errors.append(_w("error", "REQUIRED",
|
|
610
|
+
"Please make sure that the action is defined/configured properly.",
|
|
611
|
+
action, f" - {label} (row {i + 1})"))
|
|
612
|
+
return errors
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def _conditional_required(action, setting) -> list[dict]:
|
|
616
|
+
"""ref §4.8 ConditionalValidator + §5 DecisionalCardValidation."""
|
|
617
|
+
value = _g(setting, "value")
|
|
618
|
+
if not value or (isinstance(value, list) and len(value) == 0):
|
|
619
|
+
return []
|
|
620
|
+
has_errors = False
|
|
621
|
+
if isinstance(value, list):
|
|
622
|
+
for case in value:
|
|
623
|
+
if _decisional_card_invalid(case):
|
|
624
|
+
has_errors = True
|
|
625
|
+
if has_errors:
|
|
626
|
+
return [_w("error", "CASE_INCOMPLETE",
|
|
627
|
+
"Action definition incomplete. Please make sure all cases are configured.",
|
|
628
|
+
action, f" - {_label(setting)}")]
|
|
629
|
+
return []
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def _decisional_card_invalid(case: dict) -> bool:
|
|
633
|
+
"""ref §5 DecisionalCardValidation — name required, target required, >=1 condition,
|
|
634
|
+
and each condition's operator+operands filled."""
|
|
635
|
+
if not isinstance(case, dict):
|
|
636
|
+
return True
|
|
637
|
+
if not (_g(case, "name") or "").strip():
|
|
638
|
+
return True
|
|
639
|
+
if not _g(case, "target"):
|
|
640
|
+
return True
|
|
641
|
+
conditions = _g(case, "condition", default=[]) or []
|
|
642
|
+
if len(conditions) < 1:
|
|
643
|
+
return True
|
|
644
|
+
return _conditions_incomplete(conditions)
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def _conditions_incomplete(conditions: list) -> bool:
|
|
648
|
+
for cond in conditions:
|
|
649
|
+
if not isinstance(cond, dict):
|
|
650
|
+
return True
|
|
651
|
+
val = _g(cond, "value")
|
|
652
|
+
if val is None:
|
|
653
|
+
# row condition: operator + left operand required (right unless unary — unknown
|
|
654
|
+
# offline, so require left+operator which the FE always requires)
|
|
655
|
+
if not _g(cond, "operator"):
|
|
656
|
+
return True
|
|
657
|
+
left = _g(cond, "leftOperator", default={}) or {}
|
|
658
|
+
if _is_empty(_g(left, "value")):
|
|
659
|
+
return True
|
|
660
|
+
elif isinstance(val, list):
|
|
661
|
+
if len(val) == 0:
|
|
662
|
+
return True
|
|
663
|
+
if _conditions_incomplete(val):
|
|
664
|
+
return True
|
|
665
|
+
return False
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def _data_store_decisional_required(action, setting) -> list[dict]:
|
|
669
|
+
"""ref §4.9 DataStoreDecisionalValidator — Where conditions configured."""
|
|
670
|
+
value = _g(setting, "value")
|
|
671
|
+
if not isinstance(value, list) or len(value) == 0:
|
|
672
|
+
return []
|
|
673
|
+
conditions = _g(value[0], "condition", default=[]) or []
|
|
674
|
+
if len(conditions) == 0:
|
|
675
|
+
return []
|
|
676
|
+
if _conditions_incomplete(conditions):
|
|
677
|
+
return [_w("error", "WHERE_INCOMPLETE",
|
|
678
|
+
"Action definition incomplete. Please make sure the Where conditions "
|
|
679
|
+
"are configured.", action, f" - {_label(setting)}")]
|
|
680
|
+
return []
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def _delay_required(action, setting) -> list[dict]:
|
|
684
|
+
"""ref §4.10 DelayDefinitionValidator."""
|
|
685
|
+
value = _g(setting, "value")
|
|
686
|
+
errors: list[dict] = []
|
|
687
|
+
label = _label(setting)
|
|
688
|
+
delay_type = None
|
|
689
|
+
for s in _all_settings_recursive(action):
|
|
690
|
+
if _g(s, "id") == DELAY_TYPE_SETTING and _g(s, "value"):
|
|
691
|
+
delay_type = _g(s, "value")
|
|
692
|
+
break
|
|
693
|
+
if not delay_type:
|
|
694
|
+
return []
|
|
695
|
+
dval = _g(value, "value") if isinstance(value, dict) else None
|
|
696
|
+
interval = _g(value, "interval") if isinstance(value, dict) else None
|
|
697
|
+
is_wait_for = str(delay_type).lower().replace("_", " ") in ("wait for", "waitfor")
|
|
698
|
+
if not value or dval is None or dval == "" or (is_wait_for and not interval):
|
|
699
|
+
errors.append(_w("error", "REQUIRED",
|
|
700
|
+
"Please make sure that the action is defined/configured properly.",
|
|
701
|
+
action, f" - {label}"))
|
|
702
|
+
if isinstance(value, dict) and dval not in (None, ""):
|
|
703
|
+
if is_wait_for and _is_valid_number(dval) and float(dval) < 0:
|
|
704
|
+
errors.append(_w("error", "DELAY_INVALID", "Invalid delay value.",
|
|
705
|
+
action, f" - {label}"))
|
|
706
|
+
return errors
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def _data_store_mapper_required(action, setting) -> list[dict]:
|
|
710
|
+
"""Data Store `Set Values` (data-store-mapper) -- each designer row {id, left, right}
|
|
711
|
+
needs a column (`left`) and a value (`right`). It does NOT reuse the document field, so
|
|
712
|
+
it must not be validated by _document_mapper_required."""
|
|
713
|
+
value = _g(setting, "value")
|
|
714
|
+
if not value or (isinstance(value, list) and len(value) == 0):
|
|
715
|
+
return []
|
|
716
|
+
errors: list[dict] = []
|
|
717
|
+
label = _label(setting)
|
|
718
|
+
idx = 0
|
|
719
|
+
for row in (r for r in value if isinstance(r, dict)):
|
|
720
|
+
if _is_empty(_g(row, "left")) or _is_empty(_g(row, "right")):
|
|
721
|
+
errors.append(_w("error", "REQUIRED",
|
|
722
|
+
"Please make sure that the action is defined/configured properly.",
|
|
723
|
+
action, f" - {label} (row {idx + 1})"))
|
|
724
|
+
idx += 1
|
|
725
|
+
return errors
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def _document_mapper_required(action, setting) -> list[dict]:
|
|
729
|
+
"""ref §4.11 DocumentMapperValidator."""
|
|
730
|
+
value = _g(setting, "value")
|
|
731
|
+
if not value or (isinstance(value, list) and len(value) == 0):
|
|
732
|
+
return []
|
|
733
|
+
errors: list[dict] = []
|
|
734
|
+
label = _label(setting)
|
|
735
|
+
rows = [r for r in value if isinstance(r, dict)]
|
|
736
|
+
idx = 0
|
|
737
|
+
for row in rows:
|
|
738
|
+
doc = _g(row, "document")
|
|
739
|
+
if (not doc) or (doc and _has_variable(doc)):
|
|
740
|
+
if not doc or len(str(doc).strip()) == 0:
|
|
741
|
+
errors.append(_w("error", "REQUIRED",
|
|
742
|
+
"Please make sure that the action is defined/configured properly.",
|
|
743
|
+
action, f" - {label} (row {idx + 1})"))
|
|
744
|
+
idx += 1
|
|
745
|
+
return errors
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
def _column_definition_required(action, setting) -> list[dict]:
|
|
749
|
+
"""ref §4.12 ColumnDefinitionControlValidator."""
|
|
750
|
+
value = _g(setting, "value")
|
|
751
|
+
rows = _g(value, "rows") if isinstance(value, dict) else None
|
|
752
|
+
label = _label(setting)
|
|
753
|
+
if not rows or (isinstance(rows, list) and len(rows) == 0):
|
|
754
|
+
return [_w("error", "REQUIRED",
|
|
755
|
+
"Please make sure that the action is defined/configured properly.",
|
|
756
|
+
action, " - column definition")]
|
|
757
|
+
errors: list[dict] = []
|
|
758
|
+
for i, row in enumerate(rows):
|
|
759
|
+
cn = _g(row, "columnName")
|
|
760
|
+
at = _g(row, "attribute")
|
|
761
|
+
if not cn or len(str(cn).strip()) == 0 or not at or len(str(at).strip()) == 0:
|
|
762
|
+
errors.append(_w("error", "REQUIRED",
|
|
763
|
+
"Please make sure that the action is defined/configured properly.",
|
|
764
|
+
action, f" - {label} (row {i + 1})"))
|
|
765
|
+
return errors
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
def _column_definition_datatype(action, setting, var_by_id, dt_by_id) -> list[dict]:
|
|
769
|
+
"""ref §4.12 ColumnDefinitionControlValidator.doDataTypeCheck — WARNING layer."""
|
|
770
|
+
value = _g(setting, "value")
|
|
771
|
+
rows = _g(value, "rows") if isinstance(value, dict) else None
|
|
772
|
+
if not rows:
|
|
773
|
+
return []
|
|
774
|
+
probe = dict(setting)
|
|
775
|
+
probe["dataTypeId"] = STRING
|
|
776
|
+
errors: list[dict] = []
|
|
777
|
+
for i, row in enumerate(rows):
|
|
778
|
+
attr = (_g(row, "attribute") or "").strip()
|
|
779
|
+
if not validate_data_type(get_value_data_types(attr, var_by_id, dt_by_id), probe):
|
|
780
|
+
errors.append(_w("warning", "TYPE_MISMATCH", "Error: data type mismatch.",
|
|
781
|
+
action, f" - {_label(setting)} attribute (row {i + 1})"))
|
|
782
|
+
return errors
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
def _map_row_required(action, setting) -> list[dict]:
|
|
786
|
+
"""ref §4.13 MapParametersValidator / §4.14 MapProcessDataValidator (identical required)."""
|
|
787
|
+
value = _g(setting, "value")
|
|
788
|
+
label = _label(setting)
|
|
789
|
+
if isinstance(value, list) and len(value) == 0:
|
|
790
|
+
return []
|
|
791
|
+
if not value:
|
|
792
|
+
return [_w("error", "REQUIRED",
|
|
793
|
+
"Please make sure that the action is defined/configured properly.",
|
|
794
|
+
action, f" - {label} (row 1)")]
|
|
795
|
+
errors: list[dict] = []
|
|
796
|
+
for i, row in enumerate(value):
|
|
797
|
+
dest = _g(row, "destination")
|
|
798
|
+
src = _g(row, "source")
|
|
799
|
+
if not dest or (src is not None and len(str(src).strip()) == 0):
|
|
800
|
+
errors.append(_w("error", "REQUIRED",
|
|
801
|
+
"Please make sure that the action is defined/configured properly.",
|
|
802
|
+
action, f" - {label} (row {i + 1})"))
|
|
803
|
+
return errors
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
_REQUIRED_VALIDATORS: dict[str, Callable] = {
|
|
807
|
+
"ai_decisional": _ai_decisional_required,
|
|
808
|
+
"tabs_payload": _tabs_payload_required,
|
|
809
|
+
"tabs_payload_old": _tabs_payload_old_required,
|
|
810
|
+
"process_io": _process_io_required,
|
|
811
|
+
"conditional": _conditional_required,
|
|
812
|
+
"data_store_decisional": _data_store_decisional_required,
|
|
813
|
+
"delay": _delay_required,
|
|
814
|
+
"document_mapper": _document_mapper_required,
|
|
815
|
+
"data_store_mapper": _data_store_mapper_required,
|
|
816
|
+
"column_definition": _column_definition_required,
|
|
817
|
+
"map_parameters": _map_row_required,
|
|
818
|
+
"map_process_data": _map_row_required,
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
def _validator_key(setting_type: str | None) -> str:
|
|
823
|
+
return _SETTING_TYPE_TO_VALIDATOR.get(setting_type or "", "default")
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
# =============================================================================
|
|
827
|
+
# Graph-level checks (ref §2)
|
|
828
|
+
# =============================================================================
|
|
829
|
+
|
|
830
|
+
def _check_field_validation(actions, var_by_id, dt_by_id, include_types) -> list[dict]:
|
|
831
|
+
"""ref §2 checkFieldValidation — dispatch every setting to its validator."""
|
|
832
|
+
errors: list[dict] = []
|
|
833
|
+
for a in actions:
|
|
834
|
+
for setting in _settings_of(a):
|
|
835
|
+
value = _g(setting, "value")
|
|
836
|
+
stype = _g(setting, "type")
|
|
837
|
+
sid = _g(setting, "id")
|
|
838
|
+
# ref: array-valued settings are expanded to their elements, EXCEPT decisional /
|
|
839
|
+
# AI-decisional cases (validated as a whole setting).
|
|
840
|
+
if isinstance(value, list) and sid != DECISIONAL_CASE_SETTING and \
|
|
841
|
+
stype != AI_DECISIONAL_CASE:
|
|
842
|
+
items = [v for v in value if isinstance(v, dict)]
|
|
843
|
+
else:
|
|
844
|
+
items = [setting]
|
|
845
|
+
for val in items:
|
|
846
|
+
key = _validator_key(_g(val, "type"))
|
|
847
|
+
if key in _REQUIRED_VALIDATORS:
|
|
848
|
+
errors.extend(_REQUIRED_VALIDATORS[key](a, val))
|
|
849
|
+
else: # default
|
|
850
|
+
errors.extend(_default_required(a, val))
|
|
851
|
+
errors.extend(_default_value_check(a, val))
|
|
852
|
+
if include_types:
|
|
853
|
+
errors.extend(_default_datatype_check(a, val, var_by_id, dt_by_id))
|
|
854
|
+
# placeholders check runs for every setting (base class)
|
|
855
|
+
errors.extend(_placeholders_check(a, val))
|
|
856
|
+
# type layer for the two ported composite type-checks
|
|
857
|
+
if include_types and key == "column_definition":
|
|
858
|
+
errors.extend(_column_definition_datatype(a, val, var_by_id, dt_by_id))
|
|
859
|
+
return errors
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
def _check_unconnected(actions, lines_by_node, children_by_parent) -> list[dict]:
|
|
863
|
+
"""ref §2 noUnconnectedNodes."""
|
|
864
|
+
UNCONNECTED = "Please make sure that all actions are connected."
|
|
865
|
+
MISSING_END = ("Process definition incomplete. Please make sure the action is "
|
|
866
|
+
"connected to Stop action.")
|
|
867
|
+
MISSING_END_ERR = ("Process definition incomplete. Please make sure the action has any "
|
|
868
|
+
"connection besides Error path connection.")
|
|
869
|
+
kind_by_id = {_action_id(a): _kind(a) for a in actions}
|
|
870
|
+
errors: list[dict] = []
|
|
871
|
+
for a in actions:
|
|
872
|
+
nid = _action_id(a)
|
|
873
|
+
kind = kind_by_id.get(nid)
|
|
874
|
+
original = lines_by_node.get(nid, [])
|
|
875
|
+
# filterErrorPortLines: keep incoming lines always; drop outgoing error-path lines
|
|
876
|
+
filtered = [ln for ln in original if ln["dest"] == nid or not ln["error_path"]]
|
|
877
|
+
if kind in ("start", "stop"):
|
|
878
|
+
if not filtered:
|
|
879
|
+
errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
|
|
880
|
+
continue
|
|
881
|
+
if not filtered:
|
|
882
|
+
errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
|
|
883
|
+
elif len(filtered) == 1:
|
|
884
|
+
ln = filtered[0]
|
|
885
|
+
other_is_stop = kind_by_id.get(ln["source"]) == "stop" or \
|
|
886
|
+
kind_by_id.get(ln["dest"]) == "stop"
|
|
887
|
+
if other_is_stop:
|
|
888
|
+
errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
|
|
889
|
+
elif not _g(a, "ParentId", "parentId"):
|
|
890
|
+
text = MISSING_END_ERR if len(filtered) != len(original) else MISSING_END
|
|
891
|
+
errors.append(_w("error", "MISSING_STOP", text, a))
|
|
892
|
+
elif kind in ("decisional", "ai_decisional") and len(filtered) >= 2:
|
|
893
|
+
has_entry = any(ln["dest"] == nid for ln in filtered)
|
|
894
|
+
if not has_entry:
|
|
895
|
+
errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
|
|
896
|
+
elif kind == "foreach":
|
|
897
|
+
child_ids = children_by_parent.get(nid, [])
|
|
898
|
+
inner_start = next((c for c in child_ids
|
|
899
|
+
if kind_by_id.get(c) == "start"), None)
|
|
900
|
+
if inner_start and not lines_by_node.get(inner_start):
|
|
901
|
+
errors.append(_w("error", "UNCONNECTED", UNCONNECTED, a))
|
|
902
|
+
return errors
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
def _check_node_names(actions) -> list[dict]:
|
|
906
|
+
"""ref §2 checkNodeNameValidation."""
|
|
907
|
+
errors: list[dict] = []
|
|
908
|
+
for a in actions:
|
|
909
|
+
if _action_name(a) == "":
|
|
910
|
+
errors.append(_w("error", "NODE_NAME",
|
|
911
|
+
"Please make sure that all actions have a name.", a, " - node name"))
|
|
912
|
+
return errors
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
def _check_points(actions) -> list[dict]:
|
|
916
|
+
"""ref §2 checkPointsValidation — exactly 1 Start, >=1 Stop."""
|
|
917
|
+
errors: list[dict] = []
|
|
918
|
+
starts = [a for a in actions if _kind(a) == "start"]
|
|
919
|
+
stops = [a for a in actions if _kind(a) == "stop"]
|
|
920
|
+
if len(starts) != 1:
|
|
921
|
+
errors.append(_w("error", "START_COUNT",
|
|
922
|
+
"Please make sure that the process have 1 start action",
|
|
923
|
+
badges=["Start"]))
|
|
924
|
+
if len(stops) < 1:
|
|
925
|
+
errors.append(_w("error", "STOP_COUNT",
|
|
926
|
+
"Please make sure that the process have at least 1 stop action",
|
|
927
|
+
badges=["Stop"]))
|
|
928
|
+
return errors
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def _check_limits(actions) -> list[dict]:
|
|
932
|
+
"""ref §2 checkLimits — numeric settings with limits within [min, max]."""
|
|
933
|
+
errors: list[dict] = []
|
|
934
|
+
for a in actions:
|
|
935
|
+
for setting in _settings_of(a):
|
|
936
|
+
value = _g(setting, "value")
|
|
937
|
+
candidates = value if isinstance(value, list) else [setting]
|
|
938
|
+
for s in (candidates if isinstance(value, list) else [setting]):
|
|
939
|
+
lim = _g(s, "limits") if isinstance(s, dict) else None
|
|
940
|
+
sval = _g(s, "value") if isinstance(s, dict) else None
|
|
941
|
+
if not lim:
|
|
942
|
+
continue
|
|
943
|
+
lo, hi = _g(lim, "min"), _g(lim, "max")
|
|
944
|
+
if sval is None or (isinstance(sval, str) and sval.strip() == ""):
|
|
945
|
+
continue # unset value: nothing to range-check (ref: FE Number("")===0 path)
|
|
946
|
+
if not _is_valid_number(sval):
|
|
947
|
+
errors.append(_w("error", "LIMIT_NAN",
|
|
948
|
+
"Please make sure that the value is a number.",
|
|
949
|
+
a, f" - {_label(s)}"))
|
|
950
|
+
errors.append(_w("error", "LIMIT_RANGE",
|
|
951
|
+
f"Please make sure that the value is between {lo} and {hi}.",
|
|
952
|
+
a, f" - {_label(s)}"))
|
|
953
|
+
continue
|
|
954
|
+
n = float(sval)
|
|
955
|
+
if (lo is not None and n < lo) or (hi is not None and n > hi):
|
|
956
|
+
errors.append(_w("error", "LIMIT_RANGE",
|
|
957
|
+
f"Please make sure that the value is between {lo} and {hi}.",
|
|
958
|
+
a, f" - {_label(s)}"))
|
|
959
|
+
return errors
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
def _check_variables(variables) -> list[dict]:
|
|
963
|
+
"""ref §2 checkVariables — unique names + primitive-name collision."""
|
|
964
|
+
errors: list[dict] = []
|
|
965
|
+
dups: set[str] = set()
|
|
966
|
+
for v in variables:
|
|
967
|
+
name = _g(v, "Name", "name")
|
|
968
|
+
vid = _g(v, "Id", "id")
|
|
969
|
+
if name is None:
|
|
970
|
+
continue
|
|
971
|
+
clash = any(_g(o, "Name", "name") == name and _g(o, "Id", "id") != vid
|
|
972
|
+
for o in variables)
|
|
973
|
+
if clash and name not in dups:
|
|
974
|
+
errors.append(_w("error", "VAR_UNIQUE", "Variable name should be unique.",
|
|
975
|
+
badges=[f"Variable: {name}"]))
|
|
976
|
+
dups.add(name)
|
|
977
|
+
dt = _g(v, "DataType", "dataType")
|
|
978
|
+
type_name = _PRIMITIVE_NAME.get(dt, "custom")
|
|
979
|
+
low = str(name).lower()
|
|
980
|
+
if low in _TYPE_NAMES and type_name != "custom" and type_name != low:
|
|
981
|
+
errors.append(_w("error", "VAR_PRIMITIVE_NAME",
|
|
982
|
+
"Cannot name variables as Primitives with a different type.",
|
|
983
|
+
badges=[f"Variable: {name}"]))
|
|
984
|
+
return errors
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
def _check_subprocess(actions, target_vars_of) -> list[dict]:
|
|
988
|
+
"""ref §2 checkSubprocess — subprocess validity + required-input mapping + dead refs.
|
|
989
|
+
target_vars_of(flow_id) -> {var_id: var_dict}; returns {} when unavailable (skips)."""
|
|
990
|
+
if target_vars_of is None:
|
|
991
|
+
return []
|
|
992
|
+
errors: list[dict] = []
|
|
993
|
+
for a in actions:
|
|
994
|
+
if _kind(a) not in ("call_sub", "trigger_sub"):
|
|
995
|
+
continue
|
|
996
|
+
# locate the side-panel + selected subprocess id
|
|
997
|
+
sidepanel = None
|
|
998
|
+
for s in _settings_of(a):
|
|
999
|
+
if _g(s, "type") == "side-pannel":
|
|
1000
|
+
sidepanel = s
|
|
1001
|
+
break
|
|
1002
|
+
target = None
|
|
1003
|
+
for s in _walk_settings_loose(a):
|
|
1004
|
+
if _g(s, "type") == "flow-list":
|
|
1005
|
+
target = _g(s, "value")
|
|
1006
|
+
break
|
|
1007
|
+
if not target:
|
|
1008
|
+
# fall back to a GUID parameter value
|
|
1009
|
+
for p in _g(a, "parameters", "Parameters", default=[]) or []:
|
|
1010
|
+
pv = _g(p, "value", "Value")
|
|
1011
|
+
if isinstance(pv, str) and _VAR_RE.fullmatch(pv or "") and \
|
|
1012
|
+
pv != "00000000-0000-0000-0000-000000000000":
|
|
1013
|
+
target = pv
|
|
1014
|
+
break
|
|
1015
|
+
if not target:
|
|
1016
|
+
continue
|
|
1017
|
+
tvars = target_vars_of(target) or {}
|
|
1018
|
+
# process-inputs / outputs subsections
|
|
1019
|
+
inputs, outputs = [], []
|
|
1020
|
+
for sub in (_g(sidepanel, "value", default=[]) or []) if sidepanel else []:
|
|
1021
|
+
if _g(sub, "type") == "process-inputs":
|
|
1022
|
+
inputs = _g(sub, "value", default=[]) or []
|
|
1023
|
+
elif _g(sub, "type") == "process-outputs":
|
|
1024
|
+
outputs = _g(sub, "value", default=[]) or []
|
|
1025
|
+
mapped_in = {_g(r, "subprocess"): _g(r, "process") for r in inputs}
|
|
1026
|
+
for vid, v in tvars.items():
|
|
1027
|
+
if _g(v, "type", "Type") == 10 and _g(v, "isRequired", "IsRequired"):
|
|
1028
|
+
src = mapped_in.get(vid)
|
|
1029
|
+
if src is None or src == "":
|
|
1030
|
+
errors.append(_w("error", "SUB_REQ_UNMAPPED",
|
|
1031
|
+
f"Mapping of required subprocess variable "
|
|
1032
|
+
f"(<b>{_g(v, 'name', 'Name')}</b>) is missing.", a))
|
|
1033
|
+
input_ids = set(tvars.keys())
|
|
1034
|
+
for r in inputs:
|
|
1035
|
+
sub = _g(r, "subprocess") or ""
|
|
1036
|
+
for m in _VAR_RE.finditer(str(sub)):
|
|
1037
|
+
root = m.group(1).split(".")[0]
|
|
1038
|
+
if root not in input_ids:
|
|
1039
|
+
errors.append(_w("error", "SUB_CHECK_MAPPING", "Check data mapping.", a))
|
|
1040
|
+
output_ids = {vid for vid, v in tvars.items()
|
|
1041
|
+
if _g(v, "type", "Type") == 30}
|
|
1042
|
+
for r in outputs:
|
|
1043
|
+
sub = _g(r, "subprocess") or ""
|
|
1044
|
+
for m in _VAR_RE.finditer(str(sub)):
|
|
1045
|
+
root = m.group(1).split(".")[0]
|
|
1046
|
+
if root in tvars and root not in output_ids:
|
|
1047
|
+
errors.append(_w("error", "SUB_CHECK_MAPPING", "Check data mapping.", a))
|
|
1048
|
+
return errors
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
# =============================================================================
|
|
1052
|
+
# Public entrypoint
|
|
1053
|
+
# =============================================================================
|
|
1054
|
+
|
|
1055
|
+
def validate_flow(flow: dict, *, datatypes: list[dict] | None = None,
|
|
1056
|
+
target_vars_of: Callable[[str], dict] | None = None,
|
|
1057
|
+
include_types: bool = True) -> list[dict]:
|
|
1058
|
+
"""Pure FE (designer-layer) validation over a flow DTO. Returns a list of warning
|
|
1059
|
+
dicts {severity, code, text, badges, action, actionId}. severity 'error' entries
|
|
1060
|
+
block a save; 'warning' entries are the advisory type-mismatch layer.
|
|
1061
|
+
|
|
1062
|
+
- datatypes: DataModel[] catalog (for the type layer; type checks skipped if None).
|
|
1063
|
+
- target_vars_of(flow_id) -> {var_id: var}: resolver for subprocess checks (offline
|
|
1064
|
+
injectable); when None, subprocess mapping checks are skipped.
|
|
1065
|
+
- include_types: master switch for the WARNING type-mismatch layer.
|
|
1066
|
+
"""
|
|
1067
|
+
flow = _g(flow, "flow") or flow
|
|
1068
|
+
actions = [a for a in (_g(flow, "Actions", "actions", default=[]) or [])
|
|
1069
|
+
if not _g(a, "IsDisabled", "isDisabled", default=False)]
|
|
1070
|
+
variables = _g(flow, "Variables", "variables", default=[]) or []
|
|
1071
|
+
|
|
1072
|
+
var_by_id = {_g(v, "Id", "id"): v for v in variables}
|
|
1073
|
+
dt_by_id = {_g(d, "id", "Id"): d for d in (datatypes or [])}
|
|
1074
|
+
have_types = include_types and bool(datatypes)
|
|
1075
|
+
|
|
1076
|
+
lines_by_node = _build_lines(actions)
|
|
1077
|
+
children_by_parent: dict[str, list[str]] = {}
|
|
1078
|
+
for a in actions:
|
|
1079
|
+
pid = _g(a, "ParentId", "parentId")
|
|
1080
|
+
if pid:
|
|
1081
|
+
children_by_parent.setdefault(pid, []).append(_action_id(a))
|
|
1082
|
+
|
|
1083
|
+
warnings: list[dict] = []
|
|
1084
|
+
warnings.extend(_check_unconnected(actions, lines_by_node, children_by_parent))
|
|
1085
|
+
warnings.extend(_check_field_validation(actions, var_by_id, dt_by_id, have_types))
|
|
1086
|
+
warnings.extend(_check_node_names(actions))
|
|
1087
|
+
warnings.extend(_check_points(actions))
|
|
1088
|
+
warnings.extend(_check_limits(actions))
|
|
1089
|
+
warnings.extend(_check_subprocess(actions, target_vars_of))
|
|
1090
|
+
warnings.extend(_check_variables(variables))
|
|
1091
|
+
return warnings
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
def split_severity(warnings: list[dict]) -> tuple[list[dict], list[dict]]:
|
|
1095
|
+
"""(errors, warnings) — errors block a save."""
|
|
1096
|
+
errs = [w for w in warnings if w.get("severity") == "error"]
|
|
1097
|
+
warns = [w for w in warnings if w.get("severity") != "error"]
|
|
1098
|
+
return errs, warns
|