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,568 @@
|
|
|
1
|
+
"""Opt-in ELK 'layered' (Sugiyama) canvas layout — a drop-in alternative to engine.py.
|
|
2
|
+
|
|
3
|
+
WHAT THIS IS
|
|
4
|
+
------------
|
|
5
|
+
A second, independent auto-layout implementation with the *exact same I/O contract* as
|
|
6
|
+
`engine.layout` (positions = absolute node CENTERS, areas = absolute container frames,
|
|
7
|
+
plus a bbox). It maps the PROCESIO node/edge graph onto the Eclipse Layout Kernel's
|
|
8
|
+
`layered` algorithm (elkjs) and translates the result back. The legacy engine is untouched
|
|
9
|
+
and remains the default; this one is selected via the LAYOUT_ENGINE flag (see dispatch.py).
|
|
10
|
+
|
|
11
|
+
HOW TO FLIP THE FLAG
|
|
12
|
+
--------------------
|
|
13
|
+
setx LAYOUT_ENGINE elk # or: $env:LAYOUT_ENGINE = "elk" (this shell)
|
|
14
|
+
LAYOUT_ENGINE=elk python ... # bash
|
|
15
|
+
|
|
16
|
+
`dispatch.layout()` reads it: `elk` → this module; anything else / unset → legacy. Nothing
|
|
17
|
+
in the UI or tool.yaml changes.
|
|
18
|
+
|
|
19
|
+
HOW IT RUNS (process boundary)
|
|
20
|
+
------------------------------
|
|
21
|
+
The legacy engine runs backend-side, in-process, synchronously. There is no pure-Python
|
|
22
|
+
ELK, so ELK runs backend-side too: elkjs is invoked through a *synchronous* Node subprocess
|
|
23
|
+
(`elk/elk_runner.mjs`), offline (no network). If Node or elkjs is unavailable, or ELK errors
|
|
24
|
+
on a graph, this module logs and DEGRADES GRACEFULLY to the legacy engine — callers always
|
|
25
|
+
get a valid layout.
|
|
26
|
+
|
|
27
|
+
TUNING FOR READABILITY
|
|
28
|
+
----------------------
|
|
29
|
+
All ELK knobs are named constants just below, each commented. The layout is tuned for a clean,
|
|
30
|
+
untangled left-to-right process flow: NETWORK_SIMPLEX layering aligns parallel branches,
|
|
31
|
+
Brandes-Köpf BALANCED placement straightens chains, wrapping is OFF (a wide scrollable flow
|
|
32
|
+
reads better than stacked bands), and disconnected sub-flows are packed close. Top-level
|
|
33
|
+
spacing is NODE_NODE_SPACING / NODE_NODE_BETWEEN_LAYERS; inside For-Each containers it is
|
|
34
|
+
CONTAINER_BETWEEN_LAYERS / CONTAINER_NODE_NODE. To bound width instead, set
|
|
35
|
+
WRAPPING_STRATEGY="SINGLE_EDGE" and re-add an aspect ratio — at the cost of fragmenting read.
|
|
36
|
+
"""
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import json
|
|
40
|
+
import os
|
|
41
|
+
import shutil
|
|
42
|
+
import subprocess
|
|
43
|
+
import sys
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
|
|
46
|
+
from tools.procesio.layout import engine # reused ONLY for graceful fallback; never mutated
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# ELK tunables — all named + commented so the width/height tradeoff is easy to tweak.
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
ALGORITHM = "layered" # the Sugiyama layered algorithm.
|
|
52
|
+
DIRECTION = "RIGHT" # left-to-right flow, matching the PROCESIO canvas.
|
|
53
|
+
# Layering: NETWORK_SIMPLEX minimises total edge length, which ALIGNS parallel branches and
|
|
54
|
+
# keeps chains on one rank line (longest-path staggers them). The biggest readability win.
|
|
55
|
+
LAYERING_STRATEGY = "NETWORK_SIMPLEX"
|
|
56
|
+
# Wrapping OFF → a clean, uninterrupted left-to-right flow. Wrapping ("SINGLE_EDGE" +
|
|
57
|
+
# an aspect ratio) bounds width by folding long runs into stacked rows, but for process
|
|
58
|
+
# flows that fragments the read; a wide, scrollable, untangled flow is more understandable.
|
|
59
|
+
WRAPPING_STRATEGY = "OFF"
|
|
60
|
+
# Crossing minimisation: barycenter/median layer-sweep passes.
|
|
61
|
+
CROSSING_MIN_STRATEGY = "LAYER_SWEEP"
|
|
62
|
+
# Node placement: Brandes-Köpf balanced coordinates with BALANCED alignment — straightens
|
|
63
|
+
# chains and centres parallel branches (groups consecutive actions into one run).
|
|
64
|
+
NODE_PLACEMENT_STRATEGY = "BRANDES_KOEPF"
|
|
65
|
+
BK_ALIGNMENT = "BALANCED"
|
|
66
|
+
# Lay each disconnected sub-flow out on its own, then pack them CLOSE (componentComponent
|
|
67
|
+
# gap) instead of scattering them across the canvas with huge empty gaps.
|
|
68
|
+
SEPARATE_COMPONENTS = "true"
|
|
69
|
+
COMPONENT_SPACING = 50
|
|
70
|
+
# Post-compaction pulls nodes left to squeeze out slack, shortening long edges.
|
|
71
|
+
POST_COMPACTION = "LEFT"
|
|
72
|
+
# Lay compound (container) contents out in the SAME layered pass as their parents, so edges
|
|
73
|
+
# that cross hierarchy levels (a For-Each container ↔ its inner actions) are legal and routed.
|
|
74
|
+
# Without this ELK rejects a container→child edge (UnsupportedGraphException).
|
|
75
|
+
HIERARCHY_HANDLING = "INCLUDE_CHILDREN"
|
|
76
|
+
# Spacing (px). node-node = VERTICAL gap between action rows sharing a rank; between-layers =
|
|
77
|
+
# HORIZONTAL gap between ranks (also the room after a decisional, and for its branch labels).
|
|
78
|
+
# Calibrated to the user's hand-layout (row gaps ~120-160; branch labels need horizontal room).
|
|
79
|
+
NODE_NODE_SPACING = 130
|
|
80
|
+
NODE_NODE_BETWEEN_LAYERS = 120
|
|
81
|
+
# Inner padding of a compound (For-Each) container. Extra room on the LEFT leaves space for
|
|
82
|
+
# the implicit ForEach-Start, mirroring the legacy container convention. ELK format string.
|
|
83
|
+
CONTAINER_PADDING = "[top=52,left=104,bottom=100,right=48]"
|
|
84
|
+
# Spacing INSIDE a compound container. Larger than the top-level between-layers so the
|
|
85
|
+
# container's inner action chain doesn't cram its labels together (a real readability defect
|
|
86
|
+
# on For-Each bodies). Tune these up if inner labels still collide.
|
|
87
|
+
CONTAINER_BETWEEN_LAYERS = 150
|
|
88
|
+
CONTAINER_NODE_NODE = 70
|
|
89
|
+
# Minimum For-Each frame (px). A floor so a 1-2 node For-Each still has room; larger bodies
|
|
90
|
+
# grow past it from content + padding. Matches the user's hand-sized container (~W791 H200).
|
|
91
|
+
CONTAINER_MIN_W = 320
|
|
92
|
+
CONTAINER_MIN_H = 200
|
|
93
|
+
# Clustering: fold a straight run of MORE THAN CLUSTER_MIN consecutive 1-in / 1-out actions
|
|
94
|
+
# (Start/Stop included; NOT inside a For-Each; a branch/merge/error-port breaks the run) into a
|
|
95
|
+
# compact serpentine block — down a column of CLUSTER_WRAP, up the next — so a long linear chain
|
|
96
|
+
# stops sprawling the canvas wide. The block is reserved to ELK as ONE node, so no overlaps.
|
|
97
|
+
CLUSTER_ENABLED = True
|
|
98
|
+
CLUSTER_MIN = 6 # cluster runs LONGER than this (>= 7 nodes); 6-node entry spines stay
|
|
99
|
+
# horizontal, matching the reference hand-layout of the ANAF entry
|
|
100
|
+
CLUSTER_WRAP = 4 # nodes stacked per serpentine column before wrapping
|
|
101
|
+
CLUSTER_DX = 170 # serpentine column spacing (centre→centre)
|
|
102
|
+
CLUSTER_DY = 150 # serpentine row spacing (centre→centre)
|
|
103
|
+
# PROCESIO branch conventions (post-ELK): an error-port target, and a decisional's DEFAULT
|
|
104
|
+
# target that leads to a dead-end (Stop/Throw/private chain, NOT a Join and not a rejoining
|
|
105
|
+
# path), is moved PERPENDICULAR to the flow — directly above/below its source (same x) — with
|
|
106
|
+
# its private chain continuing right along that lane. Default→Join and a default that rejoins
|
|
107
|
+
# the main flow stay in-flow. PERP_LANE_GAP is the vertical offset to that lane.
|
|
108
|
+
BRANCH_CONVENTIONS = True
|
|
109
|
+
PERP_LANE_GAP = 185
|
|
110
|
+
# A DEFAULT branch only drops perpendicular when it goes ~directly to a terminal (a stop-branch,
|
|
111
|
+
# chain <= SHORT_DEADEND). A LONGER default chain is the decisional's main processing path and
|
|
112
|
+
# stays IN-FLOW. Error ports always drop perpendicular regardless of length.
|
|
113
|
+
SHORT_DEADEND = 2
|
|
114
|
+
# Fixed seed → deterministic output (same graph in → same layout out), matching the legacy
|
|
115
|
+
# engine's determinism guarantee.
|
|
116
|
+
RANDOM_SEED = 1
|
|
117
|
+
|
|
118
|
+
DEFAULT_W = 48.0
|
|
119
|
+
DEFAULT_H = 48.0
|
|
120
|
+
|
|
121
|
+
_RUNNER = Path(__file__).with_name("elk") / "elk_runner.mjs"
|
|
122
|
+
_SUBPROCESS_TIMEOUT = 60 # seconds; a runaway ELK run must not hang the tool
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _log(msg: str) -> None:
|
|
126
|
+
"""Progress/diagnostics go to stderr only (stdout is reserved for tool JSON)."""
|
|
127
|
+
print(f"[elk_engine] {msg}", file=sys.stderr)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
# node normalisation — same dimension source as the legacy engine's _node()
|
|
132
|
+
# ---------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
def _norm(n: dict) -> dict:
|
|
135
|
+
pos = n.get("position") or {"x": n.get("x", 0.0), "y": n.get("y", 0.0)}
|
|
136
|
+
w = n.get("width") or (n.get("size") or {}).get("w") or DEFAULT_W
|
|
137
|
+
h = n.get("height") or (n.get("size") or {}).get("h") or DEFAULT_H
|
|
138
|
+
try:
|
|
139
|
+
w = float(w) or DEFAULT_W
|
|
140
|
+
except (TypeError, ValueError):
|
|
141
|
+
w = DEFAULT_W
|
|
142
|
+
try:
|
|
143
|
+
h = float(h) or DEFAULT_H
|
|
144
|
+
except (TypeError, ValueError):
|
|
145
|
+
h = DEFAULT_H
|
|
146
|
+
return {
|
|
147
|
+
"id": n["id"],
|
|
148
|
+
"w": w,
|
|
149
|
+
"h": h,
|
|
150
|
+
"parent_id": n.get("parent_id"),
|
|
151
|
+
"kind": n.get("kind") or n.get("shape"),
|
|
152
|
+
"pos": {"x": float((pos or {}).get("x", 0.0) or 0.0),
|
|
153
|
+
"y": float((pos or {}).get("y", 0.0) or 0.0)},
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# ---------------------------------------------------------------------------
|
|
158
|
+
# graph translation: PROCESIO nodes/edges -> ELK graph JSON
|
|
159
|
+
# ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
def _ancestors(nid: str, parent_of: dict) -> list[str]:
|
|
162
|
+
"""[nid, parent, ..., 'root'] — the id's ancestor chain up to the synthetic root."""
|
|
163
|
+
chain = [nid]
|
|
164
|
+
seen = {nid}
|
|
165
|
+
cur = parent_of.get(nid, "root")
|
|
166
|
+
while True:
|
|
167
|
+
chain.append(cur)
|
|
168
|
+
if cur == "root" or cur in seen:
|
|
169
|
+
break
|
|
170
|
+
seen.add(cur)
|
|
171
|
+
cur = parent_of.get(cur, "root")
|
|
172
|
+
return chain
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _lca(a: str, b: str, parent_of: dict) -> str:
|
|
176
|
+
"""Lowest common ancestor of two node ids in the containment tree. An edge is declared
|
|
177
|
+
in its endpoints' LCA so hierarchy-crossing edges (container -> child) stay legal."""
|
|
178
|
+
aset = set(_ancestors(a, parent_of))
|
|
179
|
+
for x in _ancestors(b, parent_of):
|
|
180
|
+
if x in aset:
|
|
181
|
+
return x
|
|
182
|
+
return "root"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _build_elk_graph(nodes: list[dict], edges: list[dict],
|
|
186
|
+
extra_opts: dict | None = None) -> dict:
|
|
187
|
+
"""Translate normalised nodes + raw edges into the ELK graph JSON the runner consumes.
|
|
188
|
+
|
|
189
|
+
- Each action -> an ELK node with its real width/height.
|
|
190
|
+
- A node that has children -> an ELK COMPOUND node (children nested, laid out recursively
|
|
191
|
+
by ELK); its own size is left for ELK to compute from its contents.
|
|
192
|
+
- Each connection -> an ELK edge (direction preserved). Back/cycle edges are passed
|
|
193
|
+
through unchanged — the layered algorithm resolves cycles internally.
|
|
194
|
+
- Ports are intentionally NOT modelled: the PROCESIO JSON carries no per-connector
|
|
195
|
+
attachment points (edge endpoints are node ids), so inventing ports would be wrong.
|
|
196
|
+
"""
|
|
197
|
+
by_id = {n["id"]: n for n in nodes}
|
|
198
|
+
parent_of = {n["id"]: (n["parent_id"] if n["parent_id"] in by_id else "root")
|
|
199
|
+
for n in nodes}
|
|
200
|
+
children_of: dict[str, list[str]] = {"root": []}
|
|
201
|
+
for n in nodes:
|
|
202
|
+
children_of.setdefault(parent_of[n["id"]], []).append(n["id"])
|
|
203
|
+
children_of.setdefault(n["id"], children_of.get(n["id"], []))
|
|
204
|
+
|
|
205
|
+
# bucket edges by the container they must be declared in (their endpoints' LCA)
|
|
206
|
+
edges_in: dict[str, list[dict]] = {}
|
|
207
|
+
dropped = 0
|
|
208
|
+
for i, e in enumerate(sorted(edges, key=lambda e: (str(e.get("source")),
|
|
209
|
+
str(e.get("dest")),
|
|
210
|
+
int(e.get("type", 0) or 0)))):
|
|
211
|
+
s, d = e.get("source"), e.get("dest")
|
|
212
|
+
if s not in by_id or d not in by_id or s == d:
|
|
213
|
+
dropped += 1
|
|
214
|
+
continue
|
|
215
|
+
host = _lca(s, d, parent_of)
|
|
216
|
+
edges_in.setdefault(host, []).append(
|
|
217
|
+
{"id": f"e{i}", "sources": [s], "targets": [d]})
|
|
218
|
+
if dropped:
|
|
219
|
+
_log(f"dropped {dropped} edge(s) with a missing endpoint or self-loop")
|
|
220
|
+
|
|
221
|
+
def build_node(nid: str) -> dict:
|
|
222
|
+
kids = sorted(children_of.get(nid, []))
|
|
223
|
+
node: dict = {"id": nid}
|
|
224
|
+
if kids:
|
|
225
|
+
# compound: nest children, let ELK size the frame from its contents, and recurse
|
|
226
|
+
# the layered layout inside with the same tunables.
|
|
227
|
+
node["children"] = [build_node(k) for k in kids]
|
|
228
|
+
node["layoutOptions"] = {
|
|
229
|
+
"elk.padding": CONTAINER_PADDING,
|
|
230
|
+
# inherit the root's algorithm/direction/hierarchyHandling (one layered pass
|
|
231
|
+
# across the hierarchy); just size the frame from its contents.
|
|
232
|
+
"elk.hierarchyHandling": "INHERIT",
|
|
233
|
+
# roomier spacing inside the container so its inner chain's labels don't cram
|
|
234
|
+
"elk.layered.spacing.nodeNodeBetweenLayers": str(CONTAINER_BETWEEN_LAYERS),
|
|
235
|
+
"elk.spacing.nodeNode": str(CONTAINER_NODE_NODE),
|
|
236
|
+
}
|
|
237
|
+
else:
|
|
238
|
+
node["width"] = by_id[nid]["w"]
|
|
239
|
+
node["height"] = by_id[nid]["h"]
|
|
240
|
+
if nid in edges_in:
|
|
241
|
+
node["edges"] = edges_in[nid]
|
|
242
|
+
return node
|
|
243
|
+
|
|
244
|
+
root = {
|
|
245
|
+
"id": "root",
|
|
246
|
+
"layoutOptions": {
|
|
247
|
+
"elk.algorithm": ALGORITHM,
|
|
248
|
+
"elk.direction": DIRECTION,
|
|
249
|
+
"elk.hierarchyHandling": HIERARCHY_HANDLING,
|
|
250
|
+
"elk.layered.layering.strategy": LAYERING_STRATEGY,
|
|
251
|
+
"elk.layered.wrapping.strategy": WRAPPING_STRATEGY,
|
|
252
|
+
"elk.layered.crossingMinimization.strategy": CROSSING_MIN_STRATEGY,
|
|
253
|
+
"elk.layered.nodePlacement.strategy": NODE_PLACEMENT_STRATEGY,
|
|
254
|
+
"elk.layered.nodePlacement.bk.fixedAlignment": BK_ALIGNMENT,
|
|
255
|
+
"elk.separateConnectedComponents": SEPARATE_COMPONENTS,
|
|
256
|
+
"elk.spacing.componentComponent": str(COMPONENT_SPACING),
|
|
257
|
+
"elk.layered.compaction.postCompaction.strategy": POST_COMPACTION,
|
|
258
|
+
"elk.layered.spacing.nodeNodeBetweenLayers": str(NODE_NODE_BETWEEN_LAYERS),
|
|
259
|
+
"elk.spacing.nodeNode": str(NODE_NODE_SPACING),
|
|
260
|
+
"elk.spacing.edgeNode": "25",
|
|
261
|
+
"elk.layered.spacing.edgeNodeBetweenLayers": "30",
|
|
262
|
+
"elk.randomSeed": str(RANDOM_SEED),
|
|
263
|
+
},
|
|
264
|
+
"children": [build_node(nid) for nid in sorted(children_of["root"])],
|
|
265
|
+
}
|
|
266
|
+
# optional per-call overrides (opts["elk"]) — for tuning without editing the constants.
|
|
267
|
+
# A None value REMOVES that option key entirely.
|
|
268
|
+
for k, v in (extra_opts or {}).items():
|
|
269
|
+
if v is None:
|
|
270
|
+
root["layoutOptions"].pop(k, None)
|
|
271
|
+
else:
|
|
272
|
+
root["layoutOptions"][k] = str(v)
|
|
273
|
+
if "root" in edges_in:
|
|
274
|
+
root["edges"] = edges_in["root"]
|
|
275
|
+
return root
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# ---------------------------------------------------------------------------
|
|
279
|
+
# run ELK (synchronous Node subprocess) + translate coordinates back
|
|
280
|
+
# ---------------------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+
def _run_elk(graph: dict) -> dict:
|
|
283
|
+
"""Invoke elkjs via the Node runner. Returns the laid-out graph, or raises on any
|
|
284
|
+
failure (missing node/elkjs, timeout, ELK error) so the caller can fall back."""
|
|
285
|
+
node = shutil.which("node")
|
|
286
|
+
if not node:
|
|
287
|
+
raise RuntimeError("node executable not found on PATH")
|
|
288
|
+
if not _RUNNER.exists():
|
|
289
|
+
raise RuntimeError(f"elk runner missing: {_RUNNER}")
|
|
290
|
+
proc = subprocess.run(
|
|
291
|
+
[node, str(_RUNNER)],
|
|
292
|
+
input=json.dumps(graph).encode("utf-8"),
|
|
293
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
294
|
+
timeout=_SUBPROCESS_TIMEOUT,
|
|
295
|
+
)
|
|
296
|
+
out = proc.stdout.decode("utf-8", "replace").strip()
|
|
297
|
+
if not out:
|
|
298
|
+
raise RuntimeError(f"elk runner produced no output (exit {proc.returncode}); "
|
|
299
|
+
f"stderr: {proc.stderr.decode('utf-8', 'replace')[:400]}")
|
|
300
|
+
laid = json.loads(out)
|
|
301
|
+
if isinstance(laid, dict) and laid.get("error"):
|
|
302
|
+
raise RuntimeError(f"elk runner error: {laid['error']}")
|
|
303
|
+
if proc.returncode != 0:
|
|
304
|
+
raise RuntimeError(f"elk runner exit {proc.returncode}")
|
|
305
|
+
return laid
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _harvest(laid: dict) -> tuple[dict, dict]:
|
|
309
|
+
"""Walk the laid-out ELK graph, accumulating absolute offsets, and produce the legacy
|
|
310
|
+
contract: positions = absolute node CENTERS (every node incl. compounds + children),
|
|
311
|
+
areas = absolute container frames (top-left + size)."""
|
|
312
|
+
positions: dict = {}
|
|
313
|
+
areas: dict = {}
|
|
314
|
+
|
|
315
|
+
def walk(node: dict, off_x: float, off_y: float) -> None:
|
|
316
|
+
for child in node.get("children", []) or []:
|
|
317
|
+
# ELK x/y are the child's top-left RELATIVE to this node's content origin.
|
|
318
|
+
tlx = off_x + float(child.get("x", 0.0) or 0.0)
|
|
319
|
+
tly = off_y + float(child.get("y", 0.0) or 0.0)
|
|
320
|
+
kids = child.get("children") or []
|
|
321
|
+
if kids:
|
|
322
|
+
w = max(float(child.get("width", DEFAULT_W) or DEFAULT_W), CONTAINER_MIN_W)
|
|
323
|
+
h = max(float(child.get("height", DEFAULT_H) or DEFAULT_H), CONTAINER_MIN_H)
|
|
324
|
+
areas[child["id"]] = {"x": tlx, "y": tly, "width": w, "height": h}
|
|
325
|
+
positions[child["id"]] = {"x": tlx + w / 2, "y": tly + h / 2}
|
|
326
|
+
walk(child, tlx, tly) # children are positioned inside this frame
|
|
327
|
+
else:
|
|
328
|
+
w = float(child.get("width", DEFAULT_W) or DEFAULT_W)
|
|
329
|
+
h = float(child.get("height", DEFAULT_H) or DEFAULT_H)
|
|
330
|
+
positions[child["id"]] = {"x": tlx + w / 2, "y": tly + h / 2}
|
|
331
|
+
|
|
332
|
+
walk(laid, float(laid.get("x", 0.0) or 0.0), float(laid.get("y", 0.0) or 0.0))
|
|
333
|
+
return positions, areas
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# ---------------------------------------------------------------------------
|
|
337
|
+
# serpentine clustering of long straight runs (pre-collapse → ELK → expand)
|
|
338
|
+
# ---------------------------------------------------------------------------
|
|
339
|
+
|
|
340
|
+
def _serpentine_size(k: int) -> tuple[float, float]:
|
|
341
|
+
cols = (k + CLUSTER_WRAP - 1) // CLUSTER_WRAP
|
|
342
|
+
rows = min(k, CLUSTER_WRAP)
|
|
343
|
+
return (cols - 1) * CLUSTER_DX + DEFAULT_W, (rows - 1) * CLUSTER_DY + DEFAULT_H
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _find_straight_runs(ns: list[dict], edges: list[dict]) -> list[list[str]]:
|
|
347
|
+
"""Maximal chains of top-level, 1-in/1-out actions (Start/Stop included) connected by
|
|
348
|
+
private edges, NOT inside a For-Each. A branch (out>1, incl. an error port), a merge
|
|
349
|
+
(in>1), or a container boundary ends the run. Only runs longer than CLUSTER_MIN returned."""
|
|
350
|
+
by = {n["id"]: n for n in ns}
|
|
351
|
+
area_ids = {n["id"] for n in ns if n["kind"] == "area"}
|
|
352
|
+
ids = set(by)
|
|
353
|
+
indeg = {i: 0 for i in ids}
|
|
354
|
+
outdeg = {i: 0 for i in ids}
|
|
355
|
+
norm = []
|
|
356
|
+
for e in (edges or []):
|
|
357
|
+
s, d, tp = e.get("source"), e.get("dest"), int(e.get("type", 0) or 0)
|
|
358
|
+
if s in ids and d in ids and s != d:
|
|
359
|
+
outdeg[s] += 1 # counts normal AND error edges → an error handler = branch
|
|
360
|
+
indeg[d] += 1
|
|
361
|
+
if tp == 0:
|
|
362
|
+
norm.append((s, d))
|
|
363
|
+
|
|
364
|
+
def straight(nid):
|
|
365
|
+
n = by[nid]
|
|
366
|
+
return (n["kind"] != "area" and n["parent_id"] not in area_ids
|
|
367
|
+
and indeg[nid] <= 1 and outdeg[nid] <= 1)
|
|
368
|
+
|
|
369
|
+
nnext, nprev = {}, {}
|
|
370
|
+
for s, d in norm:
|
|
371
|
+
if straight(s) and straight(d):
|
|
372
|
+
nnext[s] = d
|
|
373
|
+
nprev[d] = s
|
|
374
|
+
runs, seen = [], set()
|
|
375
|
+
for s in sorted(u for u in ids if straight(u) and u in nnext and u not in nprev):
|
|
376
|
+
if s in seen:
|
|
377
|
+
continue
|
|
378
|
+
run, cur = [s], s
|
|
379
|
+
seen.add(s)
|
|
380
|
+
while cur in nnext and nnext[cur] not in seen:
|
|
381
|
+
cur = nnext[cur]
|
|
382
|
+
run.append(cur)
|
|
383
|
+
seen.add(cur)
|
|
384
|
+
if len(run) > CLUSTER_MIN:
|
|
385
|
+
runs.append(run)
|
|
386
|
+
return runs
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _collapse_runs(ns, edges, runs):
|
|
390
|
+
"""Replace each run with one meta-node sized as its serpentine block; rewire edges to it."""
|
|
391
|
+
node_to_meta, metas, meta_map = {}, [], {}
|
|
392
|
+
for k, run in enumerate(runs):
|
|
393
|
+
mid = f"__cluster{k}"
|
|
394
|
+
w, h = _serpentine_size(len(run))
|
|
395
|
+
metas.append({"id": mid, "w": w, "h": h, "parent_id": None, "kind": None,
|
|
396
|
+
"pos": {"x": 0.0, "y": 0.0}})
|
|
397
|
+
meta_map[mid] = run
|
|
398
|
+
for nid in run:
|
|
399
|
+
node_to_meta[nid] = mid
|
|
400
|
+
run_nodes = set(node_to_meta)
|
|
401
|
+
new_ns = [n for n in ns if n["id"] not in run_nodes] + metas
|
|
402
|
+
new_edges, seen_e = [], set()
|
|
403
|
+
for e in (edges or []):
|
|
404
|
+
s, d, tp = e.get("source"), e.get("dest"), int(e.get("type", 0) or 0)
|
|
405
|
+
ms, md = node_to_meta.get(s, s), node_to_meta.get(d, d)
|
|
406
|
+
if ms == md or (ms, md, tp) in seen_e:
|
|
407
|
+
continue
|
|
408
|
+
seen_e.add((ms, md, tp))
|
|
409
|
+
new_edges.append({"source": ms, "dest": md, "type": tp})
|
|
410
|
+
return new_ns, new_edges, meta_map
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def _expand_serpentine(positions: dict, mid: str, run: list[str]) -> None:
|
|
414
|
+
"""Replace a placed meta-node with its run laid out as a serpentine inside the block."""
|
|
415
|
+
c = positions.pop(mid)
|
|
416
|
+
w, h = _serpentine_size(len(run))
|
|
417
|
+
mx, my = c["x"] - w / 2, c["y"] - h / 2
|
|
418
|
+
rows = min(len(run), CLUSTER_WRAP)
|
|
419
|
+
for i, nid in enumerate(run):
|
|
420
|
+
col, r = i // CLUSTER_WRAP, i % CLUSTER_WRAP
|
|
421
|
+
row = r if col % 2 == 0 else (rows - 1 - r) # boustrophedon: down, then up
|
|
422
|
+
positions[nid] = {"x": mx + DEFAULT_W / 2 + col * CLUSTER_DX,
|
|
423
|
+
"y": my + DEFAULT_H / 2 + row * CLUSTER_DY}
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
# ---------------------------------------------------------------------------
|
|
427
|
+
# PROCESIO branch conventions: error / default-dead-end targets -> perpendicular lane
|
|
428
|
+
# ---------------------------------------------------------------------------
|
|
429
|
+
|
|
430
|
+
def _lane_collides(cand: dict, others: dict, by: dict) -> bool:
|
|
431
|
+
for cid, cp in cand.items():
|
|
432
|
+
cw = by.get(cid, {}).get("w", DEFAULT_W)
|
|
433
|
+
ch = by.get(cid, {}).get("h", DEFAULT_H)
|
|
434
|
+
for oid, op in others.items():
|
|
435
|
+
ow = by.get(oid, {}).get("w", DEFAULT_W)
|
|
436
|
+
oh = by.get(oid, {}).get("h", DEFAULT_H)
|
|
437
|
+
if abs(cp["x"] - op["x"]) < (cw + ow) / 2 and abs(cp["y"] - op["y"]) < (ch + oh) / 2 + 1:
|
|
438
|
+
return True
|
|
439
|
+
return False
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _apply_branch_conventions(positions: dict, ns: list[dict], edges: list[dict]) -> dict:
|
|
443
|
+
"""Move error / default-dead-end targets to a perpendicular lane at the source's column.
|
|
444
|
+
A dead-end = a private chain that ends in a terminal WITHOUT passing through a merge (Join);
|
|
445
|
+
so default->Join and a default that rejoins the flow return no chain and stay in-flow.
|
|
446
|
+
Loop-backs (target upstream) and For-Each members are left alone. Collision-checked; if no
|
|
447
|
+
clear lane is found the chain is left where ELK placed it (safe no-op)."""
|
|
448
|
+
by = {n["id"]: n for n in ns}
|
|
449
|
+
ids = set(by)
|
|
450
|
+
area_ids = {n["id"] for n in ns if n["kind"] == "area"}
|
|
451
|
+
|
|
452
|
+
def movable(nid):
|
|
453
|
+
return nid in by and by[nid]["kind"] != "area" and by[nid]["parent_id"] not in area_ids
|
|
454
|
+
|
|
455
|
+
norm_succ, indeg_norm = {}, {}
|
|
456
|
+
for e in edges:
|
|
457
|
+
s, d, tp = e["source"], e["dest"], int(e.get("type", 0) or 0)
|
|
458
|
+
if tp == 0 and s in ids and d in ids and s != d:
|
|
459
|
+
norm_succ.setdefault(s, []).append(d)
|
|
460
|
+
indeg_norm[d] = indeg_norm.get(d, 0) + 1
|
|
461
|
+
|
|
462
|
+
def deadend(tgt):
|
|
463
|
+
chain, cur = [], tgt
|
|
464
|
+
while True:
|
|
465
|
+
if indeg_norm.get(cur, 0) > 1: # a merge (Join) -> rejoins the flow
|
|
466
|
+
return None
|
|
467
|
+
chain.append(cur)
|
|
468
|
+
outs = [d for d in norm_succ.get(cur, []) if d in ids]
|
|
469
|
+
if not outs:
|
|
470
|
+
return chain # terminal -> private dead-end chain
|
|
471
|
+
if len(outs) > 1 or outs[0] in chain:
|
|
472
|
+
return None # branches / cycle -> not a simple dead-end
|
|
473
|
+
cur = outs[0]
|
|
474
|
+
|
|
475
|
+
DX = NODE_NODE_BETWEEN_LAYERS + DEFAULT_W
|
|
476
|
+
moves = []
|
|
477
|
+
for e in edges:
|
|
478
|
+
s, d, tp = e["source"], e["dest"], int(e.get("type", 0) or 0)
|
|
479
|
+
if s not in ids or d not in ids or s == d or s not in positions or d not in positions:
|
|
480
|
+
continue
|
|
481
|
+
if not (tp == 1 or bool(e.get("is_default"))):
|
|
482
|
+
continue
|
|
483
|
+
if positions[d]["x"] < positions[s]["x"] - 24: # loop-back -> leave in place
|
|
484
|
+
continue
|
|
485
|
+
if not movable(s):
|
|
486
|
+
continue
|
|
487
|
+
chain = deadend(d)
|
|
488
|
+
if chain and all(movable(c) for c in chain):
|
|
489
|
+
if tp == 1 or len(chain) <= SHORT_DEADEND: # error: always; default: stop-branch only
|
|
490
|
+
moves.append((s, chain))
|
|
491
|
+
|
|
492
|
+
moved = set()
|
|
493
|
+
for s, chain in sorted(moves, key=lambda m: positions[m[0]]["x"]):
|
|
494
|
+
if any(c in moved for c in chain):
|
|
495
|
+
continue
|
|
496
|
+
sx, sy = positions[s]["x"], positions[s]["y"]
|
|
497
|
+
for mult in range(1, 10):
|
|
498
|
+
done = False
|
|
499
|
+
for direction in (1, -1): # prefer below, then above
|
|
500
|
+
lane_y = sy + direction * mult * PERP_LANE_GAP
|
|
501
|
+
cand = {chain[i]: {"x": sx + i * DX, "y": lane_y} for i in range(len(chain))}
|
|
502
|
+
others = {i: positions[i] for i in positions if i not in chain}
|
|
503
|
+
if not _lane_collides(cand, others, by):
|
|
504
|
+
positions.update(cand)
|
|
505
|
+
moved.update(chain)
|
|
506
|
+
done = True
|
|
507
|
+
break
|
|
508
|
+
if done:
|
|
509
|
+
break
|
|
510
|
+
return positions
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
# ---------------------------------------------------------------------------
|
|
514
|
+
# public entry: same signature as engine.layout
|
|
515
|
+
# ---------------------------------------------------------------------------
|
|
516
|
+
|
|
517
|
+
def layout(nodes: list[dict], edges: list[dict], opts: dict | None = None,
|
|
518
|
+
subset: list[str] | None = None) -> dict:
|
|
519
|
+
"""Lay out a node/edge graph with ELK 'layered'. Same contract as engine.layout:
|
|
520
|
+
returns {positions:{id:{x,y}}, areas:{id:{x,y,width,height}}, bbox}.
|
|
521
|
+
|
|
522
|
+
`subset` (partial re-tidy) is a legacy-only capability (ELK is not incremental), so a
|
|
523
|
+
subset request is delegated to the legacy engine. Any ELK failure also falls back to
|
|
524
|
+
legacy — the caller always gets a valid layout."""
|
|
525
|
+
if subset is not None:
|
|
526
|
+
_log("subset/partial re-tidy requested → delegating to legacy engine (ELK is not "
|
|
527
|
+
"incremental)")
|
|
528
|
+
return engine.layout(nodes, edges, opts=opts, subset=subset)
|
|
529
|
+
|
|
530
|
+
ns = [_norm(n) for n in nodes]
|
|
531
|
+
if not ns:
|
|
532
|
+
return {"positions": {}, "areas": {},
|
|
533
|
+
"bbox": {"minX": 0, "minY": 0, "maxX": 0, "maxY": 0}}
|
|
534
|
+
|
|
535
|
+
# pre-collapse long straight runs into serpentine meta-nodes (ELK reserves their space)
|
|
536
|
+
runs = _find_straight_runs(ns, edges or []) if CLUSTER_ENABLED else []
|
|
537
|
+
cns, cedges, meta_map = (_collapse_runs(ns, edges or [], runs) if runs
|
|
538
|
+
else (ns, list(edges or []), {}))
|
|
539
|
+
if runs:
|
|
540
|
+
_log(f"clustered {len(runs)} straight run(s) of sizes {[len(r) for r in runs]}")
|
|
541
|
+
|
|
542
|
+
try:
|
|
543
|
+
graph = _build_elk_graph(cns, cedges, extra_opts=(opts or {}).get("elk"))
|
|
544
|
+
laid = _run_elk(graph)
|
|
545
|
+
positions, areas = _harvest(laid)
|
|
546
|
+
for mid, run in meta_map.items(): # expand each serpentine block in place
|
|
547
|
+
if mid in positions:
|
|
548
|
+
_expand_serpentine(positions, mid, run)
|
|
549
|
+
if BRANCH_CONVENTIONS:
|
|
550
|
+
_apply_branch_conventions(positions, ns, edges or [])
|
|
551
|
+
except Exception as exc: # noqa: BLE001 — any ELK failure must degrade, not crash
|
|
552
|
+
_log(f"ELK layout failed ({type(exc).__name__}: {exc}); falling back to legacy engine")
|
|
553
|
+
return engine.layout(nodes, edges, opts=opts, subset=subset)
|
|
554
|
+
|
|
555
|
+
# guard: every input node must have landed with finite coordinates; otherwise fall back
|
|
556
|
+
missing = [n["id"] for n in ns if n["id"] not in positions]
|
|
557
|
+
bad = [i for i, p in positions.items()
|
|
558
|
+
if not (isinstance(p["x"], float) and isinstance(p["y"], float))
|
|
559
|
+
or p["x"] != p["x"] or p["y"] != p["y"]] # NaN self-inequality
|
|
560
|
+
if missing or bad:
|
|
561
|
+
_log(f"ELK output incomplete (missing={missing[:5]} nan={bad[:5]}); "
|
|
562
|
+
f"falling back to legacy engine")
|
|
563
|
+
return engine.layout(nodes, edges, opts=opts, subset=subset)
|
|
564
|
+
|
|
565
|
+
xs = [p["x"] for p in positions.values()] or [0]
|
|
566
|
+
ys = [p["y"] for p in positions.values()] or [0]
|
|
567
|
+
return {"positions": positions, "areas": areas,
|
|
568
|
+
"bbox": {"minX": min(xs), "minY": min(ys), "maxX": max(xs), "maxY": max(ys)}}
|