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,336 @@
|
|
|
1
|
+
"""Rewrite the ORDERED event chains of several elements on a live form, in one save.
|
|
2
|
+
|
|
3
|
+
`form-set-element-event` appends or swaps ONE event of ONE action type. A behaviour change on a
|
|
4
|
+
multi-step form is rarely that small: a button's chain is an ordered mix of RUN_JAVASCRIPT,
|
|
5
|
+
RUN_PROCESS and MAP_FORM_DATA blocks, and the ORDER is the behaviour. Moving a MAP, adding a
|
|
6
|
+
condition to it or spreading one decision across several elements means restating whole chains,
|
|
7
|
+
and on a form with hundreds of hand-built elements that must still be surgical and atomic, not
|
|
8
|
+
a desired-state rebuild.
|
|
9
|
+
|
|
10
|
+
A PLAN lists, for each (element, trigger), the new chain in order:
|
|
11
|
+
|
|
12
|
+
{"chains": [{"element": "<name or id>", "on": "click",
|
|
13
|
+
"events": [{"keep": "<existing event id>"},
|
|
14
|
+
{"map": {"set": {"Input2.disabled": true, "@isPF": true},
|
|
15
|
+
"when": [{"field": "flag.value", "op": "EQUALS", "value": "1"},
|
|
16
|
+
{"variable": "isPF", "op": "IS_TRUE"}],
|
|
17
|
+
"match": "all"}},
|
|
18
|
+
{"js": "<code>"} | {"js_file": "<path>"},
|
|
19
|
+
{"raw": {<a complete event>}}]}]}
|
|
20
|
+
|
|
21
|
+
* `keep` re-uses an existing event of that element and trigger VERBATIM, id included, so a large
|
|
22
|
+
script is never re-sent or re-typed.
|
|
23
|
+
* A `map` target is `<element>.<configKey>` (resolved to {root}.{FIELDS_NS}.{elementId}.{configId})
|
|
24
|
+
or `@<formVariable>` (the variable id). A condition operand is `field` (`<element>` for its value,
|
|
25
|
+
`<element>.<configKey>` for any other config) or `variable`. `match` is `all` (AND) or `any` (OR).
|
|
26
|
+
* Every name, config key, variable, operator and kept id is resolved BEFORE anything is written.
|
|
27
|
+
A wrong path is otherwise saved without an error and the block silently does nothing.
|
|
28
|
+
* Events not listed in a chain are dropped, and the result names them.
|
|
29
|
+
* All chains land in ONE GET -> PUT behind the concurrency guard.
|
|
30
|
+
|
|
31
|
+
Wire shapes were copied from MAP_FORM_DATA blocks the designer itself wrote (verified on a live
|
|
32
|
+
form): an unconditional block's config is `{"mapping": [...]}`, while a conditional one adds
|
|
33
|
+
`conditions` + `areConditionsConfigured: true`. A condition's `logicOperator` links it to the NEXT
|
|
34
|
+
condition (1 = AND, 2 = OR, 0 on the last). `operandsAsListOptional` follows the operator (see
|
|
35
|
+
_LIST_OPTIONAL_OPS). Mapping values are strings: booleans become "true"/"false".
|
|
36
|
+
"""
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
import argparse
|
|
40
|
+
import copy
|
|
41
|
+
import json
|
|
42
|
+
import os
|
|
43
|
+
import uuid
|
|
44
|
+
|
|
45
|
+
from tools.procesio import formlint
|
|
46
|
+
from tools.procesio.actiondef import ActionDef
|
|
47
|
+
from tools.procesio.dto.form import fieldpath
|
|
48
|
+
from tools.procesio.errors import UsageError
|
|
49
|
+
from tools.procesio.handlers.common import add_force_arg, add_profile_arg, guard_unchanged
|
|
50
|
+
from tools.procesio.handlers.form_code import _fetch, build_put_body
|
|
51
|
+
from tools.procesio.handlers.form_events import _cfg, _find_element, _resolve_trigger
|
|
52
|
+
|
|
53
|
+
# Every operator seen in real form/process conditions (counted across exported workspaces).
|
|
54
|
+
OPERATORS = (
|
|
55
|
+
"EQUALS", "DOES_NOT_EQUAL", "CONTAINS", "DOES_NOT_CONTAIN", "IS_EMPTY", "IS_NOT_EMPTY",
|
|
56
|
+
"IS_TRUE", "IS_FALSE", "GREATER_THAN", "GREATER_THAN_OR_EQUAL_TO", "LESS_THAN",
|
|
57
|
+
"LESS_THAN_OR_EQUAL_TO", "BELONGS", "DOES_NOT_BELONG",
|
|
58
|
+
)
|
|
59
|
+
_LOGIC = {"all": 1, "and": 1, "any": 2, "or": 2}
|
|
60
|
+
# The designer sets `operandsAsListOptional` by OPERATOR, not by operand kind: counted over every
|
|
61
|
+
# condition of a large production form, it is true for exactly these four and false for
|
|
62
|
+
# CONTAINS / DOES_NOT_CONTAIN / IS_TRUE / IS_FALSE, on fields and variables alike.
|
|
63
|
+
_LIST_OPTIONAL_OPS = {"EQUALS", "DOES_NOT_EQUAL", "IS_EMPTY", "IS_NOT_EMPTY"}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _operand(value) -> dict:
|
|
67
|
+
return {"variable": "", "attribute": {"id": "", "nextAttribute": None}, "value": value}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _as_string(value) -> str:
|
|
71
|
+
if value is True:
|
|
72
|
+
return "true"
|
|
73
|
+
if value is False:
|
|
74
|
+
return "false"
|
|
75
|
+
if value is None:
|
|
76
|
+
return ""
|
|
77
|
+
return str(value)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _Resolver:
|
|
81
|
+
"""Names -> ids for one live form: element configs and form variables."""
|
|
82
|
+
|
|
83
|
+
def __init__(self, form: dict):
|
|
84
|
+
self.live = fieldpath.LiveForm(form)
|
|
85
|
+
data = form.get("data") or {}
|
|
86
|
+
self.variables = {v.get("name"): v.get("id") for v in data.get("variables") or []
|
|
87
|
+
if v.get("name") and v.get("id")}
|
|
88
|
+
|
|
89
|
+
def variable(self, name: str, where: str) -> str:
|
|
90
|
+
vid = self.variables.get(name)
|
|
91
|
+
if not vid:
|
|
92
|
+
raise UsageError(f"{where}: {name!r} is not a form variable; known: "
|
|
93
|
+
f"{', '.join(sorted(self.variables)) or '(none)'}")
|
|
94
|
+
return vid
|
|
95
|
+
|
|
96
|
+
def target(self, ref: str, where: str) -> str:
|
|
97
|
+
"""`@var` -> variable id; `<element>.<key>` -> config path (split on the LAST dot, so an
|
|
98
|
+
element name may itself contain dots)."""
|
|
99
|
+
ref = str(ref).strip()
|
|
100
|
+
if ref.startswith("@"):
|
|
101
|
+
return self.variable(ref[1:], where)
|
|
102
|
+
if "." not in ref:
|
|
103
|
+
raise UsageError(f"{where}: {ref!r} must be '<element>.<configKey>' or '@<variable>'")
|
|
104
|
+
el, key = ref.rsplit(".", 1)
|
|
105
|
+
try:
|
|
106
|
+
return self.live.config_path(el, key)
|
|
107
|
+
except UsageError as e:
|
|
108
|
+
raise UsageError(f"{where}: {e}") from e
|
|
109
|
+
|
|
110
|
+
def field(self, ref: str, where: str) -> str:
|
|
111
|
+
"""A condition's field operand: `<element>` means its value, `<element>.<key>` any config."""
|
|
112
|
+
ref = str(ref).strip()
|
|
113
|
+
try:
|
|
114
|
+
if ref in self.live.by_name or ref in self.live.by_id:
|
|
115
|
+
el = self.live.element(ref)
|
|
116
|
+
return self.live.config_path(ref, fieldpath.value_key(el.get("type")))
|
|
117
|
+
el, key = ref.rsplit(".", 1) if "." in ref else (ref, "value")
|
|
118
|
+
return self.live.config_path(el, key)
|
|
119
|
+
except UsageError as e:
|
|
120
|
+
raise UsageError(f"{where}: {e}") from e
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _conditions(when: list, match: str, res: _Resolver, where: str) -> list:
|
|
124
|
+
logic = _LOGIC.get(str(match or "all").lower())
|
|
125
|
+
if logic is None:
|
|
126
|
+
raise UsageError(f"{where}: match must be 'all' or 'any', not {match!r}")
|
|
127
|
+
out = []
|
|
128
|
+
for i, c in enumerate(when):
|
|
129
|
+
if not isinstance(c, dict):
|
|
130
|
+
raise UsageError(f"{where}: condition {i} must be an object")
|
|
131
|
+
op = str(c.get("op") or "").strip().upper()
|
|
132
|
+
if op not in OPERATORS:
|
|
133
|
+
raise UsageError(f"{where}: condition {i} op {c.get('op')!r} is not one of "
|
|
134
|
+
f"{', '.join(OPERATORS)}")
|
|
135
|
+
if "variable" in c:
|
|
136
|
+
left = res.variable(c["variable"], f"{where} condition {i}")
|
|
137
|
+
elif "field" in c:
|
|
138
|
+
left = res.field(c["field"], f"{where} condition {i}")
|
|
139
|
+
else:
|
|
140
|
+
raise UsageError(f"{where}: condition {i} needs 'field' or 'variable'")
|
|
141
|
+
out.append({
|
|
142
|
+
"id": i, "uid": str(uuid.uuid4()), "operator": op,
|
|
143
|
+
"leftOperator": _operand(left),
|
|
144
|
+
"rightOperator": _operand(_as_string(c.get("value", ""))),
|
|
145
|
+
"auxOperator": _operand(""),
|
|
146
|
+
"logicOperator": logic if i < len(when) - 1 else 0,
|
|
147
|
+
"value": None,
|
|
148
|
+
"rightOperandAsListRequired": False,
|
|
149
|
+
"operandsAsListOptional": op in _LIST_OPTIONAL_OPS,
|
|
150
|
+
})
|
|
151
|
+
return out
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _map_event(spec: dict, event_type: str, res: _Resolver, where: str) -> dict:
|
|
155
|
+
sets = spec.get("set")
|
|
156
|
+
if not isinstance(sets, dict) or not sets:
|
|
157
|
+
raise UsageError(f"{where}: a map needs a non-empty 'set' object")
|
|
158
|
+
mapping = [{"id": i, "left": res.target(k, f"{where} set {k!r}"), "right": _as_string(v)}
|
|
159
|
+
for i, (k, v) in enumerate(sets.items())]
|
|
160
|
+
config: dict = {"mapping": mapping}
|
|
161
|
+
when = spec.get("when") or []
|
|
162
|
+
if when:
|
|
163
|
+
config["conditions"] = _conditions(when, spec.get("match", "all"), res, where)
|
|
164
|
+
config["areConditionsConfigured"] = True
|
|
165
|
+
return {"id": str(uuid.uuid4()), "type": event_type, "action": "MAP_FORM_DATA",
|
|
166
|
+
"config": config}
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _js_event(code: str, event_type: str, where: str) -> dict:
|
|
170
|
+
if not isinstance(code, str) or not code.strip():
|
|
171
|
+
raise UsageError(f"{where}: empty JavaScript")
|
|
172
|
+
return {"id": str(uuid.uuid4()), "type": event_type, "action": "RUN_JAVASCRIPT",
|
|
173
|
+
"config": {"code": code}}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _summary(ev: dict, live: fieldpath.LiveForm, variables: dict) -> dict:
|
|
177
|
+
"""A human-readable line per event, so a dry run can be reviewed without reading guids."""
|
|
178
|
+
ids_to_var = {v: k for k, v in variables.items()}
|
|
179
|
+
by_cfg = {}
|
|
180
|
+
for el in live.elements:
|
|
181
|
+
name = fieldpath._name_of(el) or el.get("id")
|
|
182
|
+
for c in el.get("configs") or []:
|
|
183
|
+
by_cfg[(str(el.get("id")), str(c.get("id")))] = f"{name}.{c.get('key')}"
|
|
184
|
+
|
|
185
|
+
def name_of(path):
|
|
186
|
+
if path in ids_to_var:
|
|
187
|
+
return "@" + ids_to_var[path]
|
|
188
|
+
parts = str(path).split(".")
|
|
189
|
+
if len(parts) == fieldpath.SEGMENTS:
|
|
190
|
+
return by_cfg.get((parts[2], parts[3]), path)
|
|
191
|
+
return path
|
|
192
|
+
|
|
193
|
+
out = {"id": ev.get("id"), "action": ev.get("action")}
|
|
194
|
+
cfg = ev.get("config") or {}
|
|
195
|
+
if ev.get("action") == "MAP_FORM_DATA":
|
|
196
|
+
out["set"] = {name_of(m.get("left")): m.get("right") for m in cfg.get("mapping") or []}
|
|
197
|
+
conds = cfg.get("conditions") or []
|
|
198
|
+
if conds:
|
|
199
|
+
out["when"] = [f"{name_of(c['leftOperator'].get('value'))} {c.get('operator')} "
|
|
200
|
+
f"{c['rightOperator'].get('value')!r}" for c in conds]
|
|
201
|
+
elif ev.get("action") == "RUN_JAVASCRIPT":
|
|
202
|
+
code = cfg.get("code") or ""
|
|
203
|
+
out["js"] = f"{len(code)} chars: " + code.strip().splitlines()[0][:90] if code.strip() else ""
|
|
204
|
+
elif ev.get("action") == "RUN_PROCESS":
|
|
205
|
+
out["processId"] = cfg.get("processId")
|
|
206
|
+
return out
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _load_plan(args) -> dict:
|
|
210
|
+
raw = args.plan
|
|
211
|
+
base = os.getcwd()
|
|
212
|
+
if args.plan_file:
|
|
213
|
+
with open(args.plan_file, encoding="utf-8") as f:
|
|
214
|
+
raw = f.read()
|
|
215
|
+
base = os.path.dirname(os.path.abspath(args.plan_file))
|
|
216
|
+
if not raw:
|
|
217
|
+
raise UsageError("provide --plan '<json>' or --plan-file <path>")
|
|
218
|
+
try:
|
|
219
|
+
plan = json.loads(raw)
|
|
220
|
+
except json.JSONDecodeError as e:
|
|
221
|
+
raise UsageError(f"the plan must be valid JSON: {e}") from e
|
|
222
|
+
if not isinstance(plan, dict) or not isinstance(plan.get("chains"), list) or not plan["chains"]:
|
|
223
|
+
raise UsageError("the plan must be an object with a non-empty 'chains' list")
|
|
224
|
+
plan["_base"] = base
|
|
225
|
+
return plan
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def set_element_chains(client, args) -> dict:
|
|
229
|
+
plan = _load_plan(args)
|
|
230
|
+
form = _fetch(client, args.id)
|
|
231
|
+
data = form["data"]
|
|
232
|
+
elements = data.get("elements") or []
|
|
233
|
+
count_before = len(elements)
|
|
234
|
+
res = _Resolver(form)
|
|
235
|
+
|
|
236
|
+
staged, report, seen, warnings = [], [], set(), []
|
|
237
|
+
for n, chain in enumerate(plan["chains"]):
|
|
238
|
+
where = f"chains[{n}]"
|
|
239
|
+
if not isinstance(chain, dict) or not chain.get("element") or not chain.get("on"):
|
|
240
|
+
raise UsageError(f"{where}: needs 'element' and 'on'")
|
|
241
|
+
element = _find_element(elements, str(chain["element"]))
|
|
242
|
+
cfg_key, event_type = _resolve_trigger(str(chain["on"]))
|
|
243
|
+
if (element.get("id"), cfg_key) in seen:
|
|
244
|
+
raise UsageError(f"{where}: {chain['element']!r} {cfg_key} appears twice in the plan")
|
|
245
|
+
seen.add((element.get("id"), cfg_key))
|
|
246
|
+
slot = _cfg(element, cfg_key)
|
|
247
|
+
if slot is None:
|
|
248
|
+
raise UsageError(f"{where}: element {chain['element']!r} has no {cfg_key!r} config")
|
|
249
|
+
current = slot.get("value") or {}
|
|
250
|
+
previous = list(current.get("events") or [])
|
|
251
|
+
by_id = {e.get("id"): e for e in previous}
|
|
252
|
+
|
|
253
|
+
new_events, kept = [], []
|
|
254
|
+
for i, spec in enumerate(chain.get("events") or []):
|
|
255
|
+
w = f"{where}.events[{i}] ({chain['element']})"
|
|
256
|
+
if not isinstance(spec, dict) or len(spec) != 1:
|
|
257
|
+
raise UsageError(f"{w}: each event is one of keep / map / js / js_file / raw")
|
|
258
|
+
kind, body = next(iter(spec.items()))
|
|
259
|
+
if kind == "keep":
|
|
260
|
+
if body not in by_id:
|
|
261
|
+
raise UsageError(f"{w}: no event {body!r} on this element's {cfg_key}; it has "
|
|
262
|
+
f"{', '.join(str(k) for k in by_id) or '(none)'}")
|
|
263
|
+
if body in kept:
|
|
264
|
+
raise UsageError(f"{w}: event {body!r} is kept twice")
|
|
265
|
+
kept.append(body)
|
|
266
|
+
new_events.append(copy.deepcopy(by_id[body]))
|
|
267
|
+
elif kind == "map":
|
|
268
|
+
new_events.append(_map_event(body, event_type, res, w))
|
|
269
|
+
elif kind == "js":
|
|
270
|
+
new_events.append(_js_event(body, event_type, w))
|
|
271
|
+
elif kind == "js_file":
|
|
272
|
+
path = body if os.path.isabs(body) else os.path.join(plan["_base"], body)
|
|
273
|
+
with open(path, encoding="utf-8") as f:
|
|
274
|
+
new_events.append(_js_event(f.read(), event_type, w))
|
|
275
|
+
elif kind == "raw":
|
|
276
|
+
if not isinstance(body, dict) or not body.get("action"):
|
|
277
|
+
raise UsageError(f"{w}: raw needs a complete event object")
|
|
278
|
+
ev = copy.deepcopy(body)
|
|
279
|
+
ev.setdefault("id", str(uuid.uuid4()))
|
|
280
|
+
ev.setdefault("type", event_type)
|
|
281
|
+
new_events.append(ev)
|
|
282
|
+
else:
|
|
283
|
+
raise UsageError(f"{w}: unknown event kind {kind!r}")
|
|
284
|
+
|
|
285
|
+
staged.append((slot, {"debounce": current.get("debounce", 0), "events": new_events}))
|
|
286
|
+
warnings += formlint.string_boolean_conditions(
|
|
287
|
+
new_events, data.get("variables"), f"{chain['element']} {cfg_key}")
|
|
288
|
+
report.append({
|
|
289
|
+
"element": chain["element"], "elementId": element.get("id"), "trigger": cfg_key,
|
|
290
|
+
"previous": [_summary(e, res.live, res.variables) for e in previous],
|
|
291
|
+
"events": [_summary(e, res.live, res.variables) for e in new_events],
|
|
292
|
+
"kept": kept,
|
|
293
|
+
"dropped": [e.get("id") for e in previous if e.get("id") not in kept],
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
result = {"id": args.id, "chains": report, "elements": count_before}
|
|
297
|
+
if warnings:
|
|
298
|
+
result["warnings"] = warnings
|
|
299
|
+
if args.dry_run:
|
|
300
|
+
return {"dry_run": True, **result}
|
|
301
|
+
|
|
302
|
+
guard = guard_unchanged(lambda: _fetch(client, args.id), form, force=args.force)
|
|
303
|
+
for slot, value in staged:
|
|
304
|
+
slot["value"] = value
|
|
305
|
+
if len(data.get("elements") or []) != count_before:
|
|
306
|
+
raise UsageError("element count changed while staging; refusing to PUT")
|
|
307
|
+
client.put("/api/FormTemplate", build_put_body(form, data=data))
|
|
308
|
+
return {"updated": True, "concurrency": guard, **result}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _args(p: argparse.ArgumentParser) -> None:
|
|
312
|
+
add_profile_arg(p)
|
|
313
|
+
p.add_argument("--id", required=True, help="form template id")
|
|
314
|
+
p.add_argument("--plan", help="the chains plan as JSON (see the action description)")
|
|
315
|
+
p.add_argument("--plan-file", dest="plan_file",
|
|
316
|
+
help="path to the plan JSON; js_file paths inside it resolve relative to it")
|
|
317
|
+
p.add_argument("--dry-run", dest="dry_run", action="store_true",
|
|
318
|
+
help="resolve and report every chain (names, not guids) without saving")
|
|
319
|
+
add_force_arg(p)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
ACTIONS = {
|
|
323
|
+
"form-set-element-chains": ActionDef(
|
|
324
|
+
func=set_element_chains, add_args=_args, needs_client=True,
|
|
325
|
+
description="Rewrite the ORDERED event chains of one or more elements on a live form in "
|
|
326
|
+
"ONE save (surgical: only the listed element triggers change). Each chain "
|
|
327
|
+
"lists its events in order: {keep: <existing event id>} re-uses an event "
|
|
328
|
+
"verbatim (large scripts are never re-sent), {map: {set: {'<element>."
|
|
329
|
+
"<configKey>': value, '@<variable>': value}, when: [{field|variable, op, "
|
|
330
|
+
"value}], match: all|any}} builds a MAP_FORM_DATA block from NAMES, {js: "
|
|
331
|
+
"code} / {js_file: path} adds a RUN_JAVASCRIPT block, {raw: event} passes an "
|
|
332
|
+
"event through. Every name, config key, variable, operator and kept id is "
|
|
333
|
+
"resolved before writing; events not listed are dropped and reported. "
|
|
334
|
+
"--dry-run prints each chain by name.",
|
|
335
|
+
),
|
|
336
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""Surgical read/write of a form's global CSS + JavaScript (`Data.code`).
|
|
2
|
+
|
|
3
|
+
Why this exists as its own pair of actions rather than going through `form-edit`:
|
|
4
|
+
`form-edit` is a DESIRED-STATE editor — it rebuilds the whole DTO from a config, so
|
|
5
|
+
using it to change one field on a large hand-authored form means re-declaring every
|
|
6
|
+
element or losing them. Restyling an existing form is the common case, so these two
|
|
7
|
+
actions do a read-modify-write on the LIVE DTO and touch ONLY `Data.code`, leaving
|
|
8
|
+
the element tree, theme, data model and events byte-identical.
|
|
9
|
+
|
|
10
|
+
`Data.code` is the designer's "Switch to code" editor and is stored AES-encrypted
|
|
11
|
+
(see dto/form/code_cipher.py + FORM-STYLING-NOTES.md); there is no plaintext field
|
|
12
|
+
on the DTO, so both directions need the `form-code-key` secret. The CSS is injected
|
|
13
|
+
by the renderer as a `<style>` prepended inside the form root; the JS is replayed as
|
|
14
|
+
a synthetic RUN_JAVASCRIPT event on mount and on every change, so it must be
|
|
15
|
+
idempotent — it runs more than once per page.
|
|
16
|
+
|
|
17
|
+
`form-set-code` always returns the PREVIOUS decrypted css/js under `previous`, so an
|
|
18
|
+
overwrite is recoverable from the tool output alone.
|
|
19
|
+
|
|
20
|
+
Concurrency (applies to EVERY surgical form/process action): each is a read-modify-write
|
|
21
|
+
of the WHOLE live DTO via PUT — there is no version/ETag check, so it is last-write-wins.
|
|
22
|
+
Two surgical writes racing each other, or one racing a designer "Save", silently clobber
|
|
23
|
+
whichever landed first. Safe for the intended single-editor CLI use; a concurrent editor
|
|
24
|
+
is the caller's responsibility. An optimistic-concurrency guard is tracked in
|
|
25
|
+
todo/procesio-surgical-optimistic-concurrency.md.
|
|
26
|
+
"""
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
|
|
31
|
+
from tools.procesio import formlint
|
|
32
|
+
from tools.procesio.actiondef import ActionDef
|
|
33
|
+
from tools.procesio.errors import NotPersisted, UsageError
|
|
34
|
+
from tools.procesio.handlers.common import add_force_arg, add_profile_arg, guard_unchanged
|
|
35
|
+
|
|
36
|
+
# Top-level DTO keys PUT /api/FormTemplate expects (PascalCase), mapped from the
|
|
37
|
+
# camelCase keys GET /api/FormTemplate/{id} returns. Kept explicit so an unexpected
|
|
38
|
+
# extra field on the GET is never echoed back into the PUT.
|
|
39
|
+
_PUT_KEYS = {
|
|
40
|
+
"Id": "id",
|
|
41
|
+
"Name": "name",
|
|
42
|
+
"IsPrivate": "isPrivate",
|
|
43
|
+
"Type": "type",
|
|
44
|
+
"Status": "status",
|
|
45
|
+
"State": "state",
|
|
46
|
+
"Assignees": "assignees",
|
|
47
|
+
"CustomUrl": "customUrl",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def build_put_body(form: dict, *, data: dict | None = None, name: str | None = None,
|
|
52
|
+
status: int | None = None, state: bool | None = None,
|
|
53
|
+
is_private: bool | None = None, assignees: list | None = None) -> dict:
|
|
54
|
+
"""The single canonical GET-echo -> accepted-PUT-body transform for form surgery.
|
|
55
|
+
|
|
56
|
+
`PUT /api/FormTemplate` binds top-level keys by PascalCase (`Id`/`Name`/`Data`);
|
|
57
|
+
the camelCase GET echo (`id`/`name`/`data`) is rejected 403. Every surgical
|
|
58
|
+
save (set-code, set-element-event/config, form-update) routes through here so
|
|
59
|
+
no caller hand-rolls the camelCase PUT the server refuses.
|
|
60
|
+
|
|
61
|
+
`Data` round-trips verbatim from the echo unless `data` is given (the caller
|
|
62
|
+
already merged its change in). Top-level `name`/`status`/`state` are overridden
|
|
63
|
+
only when passed. `IsPrivate` is the reachability switch: `false` + `Status: 1` is what makes a
|
|
64
|
+
form answer to an anonymous visitor on its custom URL, and it is a PUBLISHING decision, so it is
|
|
65
|
+
never changed unless the caller asks. `CustomUrl` is ECHOED from the GET (the PUT ignores it either
|
|
66
|
+
way — a form's custom URL is a separate entity managed via /api/CustomUrl/*, so
|
|
67
|
+
echo and null are equivalent; verified live), and server metadata that isn't in
|
|
68
|
+
`_PUT_KEYS` is dropped.
|
|
69
|
+
"""
|
|
70
|
+
for k in ("id", "name", "data"):
|
|
71
|
+
if not form.get(k):
|
|
72
|
+
raise UsageError(f"GET /api/FormTemplate echo is missing {k!r}; refusing to PUT")
|
|
73
|
+
dto = {k: form.get(src) for k, src in _PUT_KEYS.items()} # PascalCase echo incl. CustomUrl
|
|
74
|
+
dto["Data"] = form.get("data") or {} if data is None else data
|
|
75
|
+
if name is not None:
|
|
76
|
+
dto["Name"] = name
|
|
77
|
+
if status is not None:
|
|
78
|
+
dto["Status"] = status
|
|
79
|
+
if state is not None:
|
|
80
|
+
dto["State"] = bool(state)
|
|
81
|
+
if is_private is not None:
|
|
82
|
+
dto["IsPrivate"] = bool(is_private)
|
|
83
|
+
if assignees is not None: # re-derived task routing (dto.form.builder.reconcile_assignees)
|
|
84
|
+
dto["Assignees"] = assignees
|
|
85
|
+
return dto
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _code_key(client) -> str:
|
|
89
|
+
"""The form-code-key for the environment this call runs against: one per
|
|
90
|
+
installation, never borrowed from another (form_code_keys)."""
|
|
91
|
+
from tools.procesio import form_code_keys
|
|
92
|
+
env = getattr(client, "env", None)
|
|
93
|
+
return form_code_keys.key_for(env.get("name") if isinstance(env, dict) else None)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _fetch(client, form_id: str) -> dict:
|
|
97
|
+
body = client.get(f"/api/FormTemplate/{form_id}")
|
|
98
|
+
if isinstance(body, dict) and "result" in body and isinstance(body["result"], dict):
|
|
99
|
+
body = body["result"]
|
|
100
|
+
if not isinstance(body, dict) or "data" not in body:
|
|
101
|
+
raise UsageError(f"unexpected FormTemplate payload for {form_id!r}")
|
|
102
|
+
return body
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _decode(blob: str, key: str) -> dict:
|
|
106
|
+
"""Decrypt a Data.code blob into {"CSS": str, "JAVASCRIPT": str}.
|
|
107
|
+
|
|
108
|
+
Two shapes both mean "no form-level code" and must not be treated as errors:
|
|
109
|
+
an empty/absent blob, and a blob that decrypts to explicit JSON nulls — the
|
|
110
|
+
latter is the designer's INITIAL state (the frontend seeds both slots to null),
|
|
111
|
+
so it is what every form that has never used "Switch to code" carries. Both are
|
|
112
|
+
normalized to empty strings here so callers can rely on getting strings.
|
|
113
|
+
"""
|
|
114
|
+
if not blob:
|
|
115
|
+
return {"CSS": "", "JAVASCRIPT": ""}
|
|
116
|
+
from tools.procesio.dto.form import code_cipher
|
|
117
|
+
try:
|
|
118
|
+
raw = code_cipher.decrypt_code(blob, key)
|
|
119
|
+
except Exception as e: # noqa: BLE001 — wrong key and corrupt blob look alike
|
|
120
|
+
raise UsageError(
|
|
121
|
+
f"could not decrypt Data.code ({e}); the form-code-key stored for this "
|
|
122
|
+
f"environment does not match the blob this form was saved with (one key per "
|
|
123
|
+
f"installation: check it is THIS installation's)"
|
|
124
|
+
) from e
|
|
125
|
+
return {"CSS": raw.get("CSS") or "", "JAVASCRIPT": raw.get("JAVASCRIPT") or ""}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _read_text(inline, path, label: str):
|
|
129
|
+
if inline is not None and path:
|
|
130
|
+
raise UsageError(f"pass either --{label} or --{label}-file, not both")
|
|
131
|
+
if path:
|
|
132
|
+
with open(path, encoding="utf-8") as f:
|
|
133
|
+
return f.read()
|
|
134
|
+
return inline
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# -- actions -----------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
def get_code(client, args) -> dict:
|
|
140
|
+
form = _fetch(client, args.id)
|
|
141
|
+
code = _decode(form["data"].get("code") or "", _code_key(client))
|
|
142
|
+
css, js = code.get("CSS", ""), code.get("JAVASCRIPT", "")
|
|
143
|
+
if args.css_out:
|
|
144
|
+
with open(args.css_out, "w", encoding="utf-8") as f:
|
|
145
|
+
f.write(css)
|
|
146
|
+
if args.js_out:
|
|
147
|
+
with open(args.js_out, "w", encoding="utf-8") as f:
|
|
148
|
+
f.write(js)
|
|
149
|
+
return {"id": args.id, "name": form.get("name"), "css": css, "javascript": js,
|
|
150
|
+
"css_bytes": len(css), "javascript_bytes": len(js)}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def set_code(client, args) -> dict:
|
|
154
|
+
css = _read_text(args.css, args.css_file, "css")
|
|
155
|
+
js = _read_text(args.javascript, args.js_file, "js")
|
|
156
|
+
|
|
157
|
+
if getattr(args, "clear", False):
|
|
158
|
+
# Recovery path. Every other route through this action DECRYPTS the existing blob first, so
|
|
159
|
+
# that omitting one side preserves it — which means a form saved under a different passphrase
|
|
160
|
+
# (a rotated key, another workspace, an import) becomes permanently uneditable: the decrypt
|
|
161
|
+
# fails before any write can happen. Clearing is the only repair that does not need the
|
|
162
|
+
# original key, so it deliberately never touches the cipher. The discarded blob is reported
|
|
163
|
+
# by LENGTH only; a value that cannot be decrypted must not be echoed either.
|
|
164
|
+
if css is not None or js is not None:
|
|
165
|
+
raise UsageError("--clear removes the form's code entirely; do not combine it with "
|
|
166
|
+
"--css/--css-file or --javascript/--js-file")
|
|
167
|
+
form = _fetch(client, args.id)
|
|
168
|
+
old = form["data"].get("code") or ""
|
|
169
|
+
result = {"id": args.id, "name": form.get("name"), "cleared": True,
|
|
170
|
+
"discarded_blob_bytes": len(old),
|
|
171
|
+
"elements": len(form["data"].get("elements") or [])}
|
|
172
|
+
if args.dry_run:
|
|
173
|
+
return {"dry_run": True, **result}
|
|
174
|
+
guard = guard_unchanged(lambda: _fetch(client, args.id), form, force=args.force)
|
|
175
|
+
data = dict(form["data"])
|
|
176
|
+
data["code"] = ""
|
|
177
|
+
client.put("/api/FormTemplate", build_put_body(form, data=data))
|
|
178
|
+
return {"updated": True, "concurrency": guard, **result}
|
|
179
|
+
|
|
180
|
+
if css is None and js is None:
|
|
181
|
+
raise UsageError("nothing to set: pass --css/--css-file and/or --javascript/--js-file, "
|
|
182
|
+
"or --clear to remove the form's code entirely")
|
|
183
|
+
|
|
184
|
+
key = _code_key(client)
|
|
185
|
+
form = _fetch(client, args.id)
|
|
186
|
+
previous = _decode(form["data"].get("code") or "", key)
|
|
187
|
+
# Omitting one side PRESERVES it — restyling should never silently drop the JS.
|
|
188
|
+
css = previous["CSS"] if css is None else css
|
|
189
|
+
js = previous["JAVASCRIPT"] if js is None else js
|
|
190
|
+
|
|
191
|
+
from tools.procesio.dto.form import code_cipher
|
|
192
|
+
blob = code_cipher.encrypt_code(js, css, key) if (css or js) else ""
|
|
193
|
+
|
|
194
|
+
result = {
|
|
195
|
+
"id": args.id, "name": form.get("name"),
|
|
196
|
+
"css_bytes": len(css), "javascript_bytes": len(js),
|
|
197
|
+
"previous": {"css": previous["CSS"], "javascript": previous["JAVASCRIPT"]},
|
|
198
|
+
"elements": len(form["data"].get("elements") or []),
|
|
199
|
+
}
|
|
200
|
+
warnings = formlint.lint_form_code_scope(css, js)
|
|
201
|
+
if warnings:
|
|
202
|
+
result["warnings"] = warnings
|
|
203
|
+
if args.dry_run:
|
|
204
|
+
return {"dry_run": True, **result}
|
|
205
|
+
|
|
206
|
+
guard = guard_unchanged(lambda: _fetch(client, args.id), form, force=args.force)
|
|
207
|
+
data = dict(form["data"])
|
|
208
|
+
data["code"] = blob
|
|
209
|
+
client.put("/api/FormTemplate", build_put_body(form, data=data))
|
|
210
|
+
verified = _verify_persisted(client, args.id, key, css, js, previous)
|
|
211
|
+
return {"updated": True, "verified": verified, "concurrency": guard, **result}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# Seen live (2026-10-18, Internal-PROD): POST /api/FormTemplate, PUT /api/FormTemplate and
|
|
215
|
+
# .../duplicate all answered 2xx while dropping Data entirely: a new form or a duplicate came back
|
|
216
|
+
# with data: null, and an update moved updatedOn but left every Data field (code, browserTitle)
|
|
217
|
+
# as it was. A 2xx alone therefore proves nothing about a form write; read it back.
|
|
218
|
+
def _verify_persisted(client, form_id: str, key: str, css: str, js: str, previous: dict):
|
|
219
|
+
"""Read the form back and compare. True = stored as sent; None = the read-back was not a
|
|
220
|
+
form payload (cannot tell); raises NotPersisted when the server kept something else."""
|
|
221
|
+
try:
|
|
222
|
+
after = _fetch(client, form_id)
|
|
223
|
+
except UsageError:
|
|
224
|
+
return None
|
|
225
|
+
got = _decode(after["data"].get("code") or "", key)
|
|
226
|
+
if got["CSS"] == css and got["JAVASCRIPT"] == js:
|
|
227
|
+
return True
|
|
228
|
+
kept_previous = got["CSS"] == previous["CSS"] and got["JAVASCRIPT"] == previous["JAVASCRIPT"]
|
|
229
|
+
raise NotPersisted(
|
|
230
|
+
"the PUT was accepted but the form still holds "
|
|
231
|
+
+ ("its PREVIOUS code. " if kept_previous else "different code. ")
|
|
232
|
+
+ ("PROCESIO has been seen accepting form writes while dropping Data altogether: create a tiny "
|
|
233
|
+
"test form and read it back - data: null there means the platform is not storing form "
|
|
234
|
+
"content, and no retry will help. Otherwise another editor saved the form at the same time."),
|
|
235
|
+
{"sent_bytes": len(css.encode("utf-8")) + len(js.encode("utf-8")),
|
|
236
|
+
"stored_bytes": len(got["CSS"].encode("utf-8")) + len(got["JAVASCRIPT"].encode("utf-8")),
|
|
237
|
+
"kept_previous": kept_previous})
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _get_args(p: argparse.ArgumentParser) -> None:
|
|
241
|
+
add_profile_arg(p)
|
|
242
|
+
p.add_argument("--id", required=True, help="form template id")
|
|
243
|
+
p.add_argument("--css-out", dest="css_out", help="also write the CSS to this file")
|
|
244
|
+
p.add_argument("--js-out", dest="js_out", help="also write the JavaScript to this file")
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _set_args(p: argparse.ArgumentParser) -> None:
|
|
248
|
+
add_profile_arg(p)
|
|
249
|
+
p.add_argument("--id", required=True, help="form template id")
|
|
250
|
+
p.add_argument("--css", help="CSS source as an inline string")
|
|
251
|
+
p.add_argument("--css-file", dest="css_file", help="path to a .css file")
|
|
252
|
+
p.add_argument("--javascript", help="JavaScript source as an inline string")
|
|
253
|
+
p.add_argument("--js-file", dest="js_file", help="path to a .js file")
|
|
254
|
+
p.add_argument("--clear", action="store_true",
|
|
255
|
+
help="remove the form's code entirely without decrypting it - the only repair "
|
|
256
|
+
"for a blob the stored form-code-key cannot open")
|
|
257
|
+
p.add_argument("--dry-run", dest="dry_run", action="store_true",
|
|
258
|
+
help="report the change (and the previous code) without writing")
|
|
259
|
+
add_force_arg(p)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
ACTIONS = {
|
|
263
|
+
"form-get-code": ActionDef(
|
|
264
|
+
func=get_code, add_args=_get_args, needs_client=True,
|
|
265
|
+
description="Read a form's global CSS + JavaScript (decrypts Data.code).",
|
|
266
|
+
),
|
|
267
|
+
"form-set-code": ActionDef(
|
|
268
|
+
func=set_code, add_args=_set_args, needs_client=True,
|
|
269
|
+
description="Set a form's global CSS + JavaScript in place (surgical: only Data.code changes; "
|
|
270
|
+
"omitted side is preserved; returns the previous code). Reads the form back after the "
|
|
271
|
+
"write and fails with not_persisted if the server kept other code (PROCESIO has been seen "
|
|
272
|
+
"answering 2xx while dropping the form's Data). --clear removes the code "
|
|
273
|
+
"entirely WITHOUT decrypting it, which is the only repair for a blob the stored "
|
|
274
|
+
"form-code-key cannot open. Returns `warnings` when the code mounts UI on "
|
|
275
|
+
"document.body or styles :root/html/body without propagating the form's CSS "
|
|
276
|
+
"scope attribute (those styles do not apply)."),
|
|
277
|
+
}
|