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.
Files changed (647) hide show
  1. procesio_cli/__init__.py +18 -0
  2. procesio_cli/_cli.py +71 -0
  3. procesio_cli/agents/__init__.py +0 -0
  4. procesio_cli/agents/_lib/__init__.py +0 -0
  5. procesio_cli/agents/_lib/actiondef.py +24 -0
  6. procesio_cli/agents/_lib/errors.py +40 -0
  7. procesio_cli/agents/_lib/reversibility.py +236 -0
  8. procesio_cli/agents/_lib/toolrunner.py +141 -0
  9. procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PLAYBOOK.md +62 -0
  10. procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-PROCESIO-INTEROP.md +76 -0
  11. procesio_cli/agents/connector-builder/CONNECTOR-BUILDER-TROUBLESHOOTING.md +60 -0
  12. procesio_cli/agents/connector-builder/README.md +51 -0
  13. procesio_cli/agents/connector-builder/agent.yaml +61 -0
  14. procesio_cli/agents/connector-builder/context.py +19 -0
  15. procesio_cli/agents/connector-builder/handlers/__init__.py +1 -0
  16. procesio_cli/agents/connector-builder/handlers/checklist.py +71 -0
  17. procesio_cli/agents/connector-builder/handlers/guidance.py +37 -0
  18. procesio_cli/agents/connector-builder/handlers/nextstep.py +221 -0
  19. procesio_cli/agents/connector-builder/handlers/status.py +35 -0
  20. procesio_cli/agents/connector-builder/knowledge.py +47 -0
  21. procesio_cli/agents/connector-builder/main.py +93 -0
  22. procesio_cli/agents/connector-builder/tests/conftest.py +95 -0
  23. procesio_cli/agents/connector-builder/tests/test_cba_agent.py +148 -0
  24. procesio_cli/agents/orchestrator/__init__.py +0 -0
  25. procesio_cli/agents/orchestrator/bootstraplib.py +166 -0
  26. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/00-INDEX.md +60 -0
  27. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/01-PROCESSES.md +230 -0
  28. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/02-FORMS.md +287 -0
  29. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/03-INTEGRATIONS.md +384 -0
  30. procesio_cli/agents/procesio/IMPLEMENTATION-PATTERNS/04-DATABASE.md +293 -0
  31. procesio_cli/agents/procesio/PROCESIO-API-RELIABILITY-DOCTRINE.md +113 -0
  32. procesio_cli/agents/procesio/PROCESIO-BEST-PRACTICES.md +316 -0
  33. procesio_cli/agents/procesio/PROCESIO-BUILD-AND-TEST-PLAYBOOK.md +217 -0
  34. procesio_cli/agents/procesio/PROCESIO-DATASTORE.md +72 -0
  35. procesio_cli/agents/procesio/PROCESIO-ENVIRONMENTS.md +62 -0
  36. procesio_cli/agents/procesio/PROCESIO-SCHEDULING.md +47 -0
  37. procesio_cli/agents/procesio/PROCESIO-VISUAL-ORGANIZATION.md +175 -0
  38. procesio_cli/agents/procesio/README.md +97 -0
  39. procesio_cli/agents/procesio/__init__.py +0 -0
  40. procesio_cli/agents/procesio/agent.yaml +73 -0
  41. procesio_cli/agents/procesio/audit.py +226 -0
  42. procesio_cli/agents/procesio/checklist.py +135 -0
  43. procesio_cli/agents/procesio/context.py +19 -0
  44. procesio_cli/agents/procesio/handlers/__init__.py +0 -0
  45. procesio_cli/agents/procesio/handlers/audit.py +39 -0
  46. procesio_cli/agents/procesio/handlers/checklist.py +34 -0
  47. procesio_cli/agents/procesio/handlers/guidance.py +36 -0
  48. procesio_cli/agents/procesio/handlers/status.py +33 -0
  49. procesio_cli/agents/procesio/handlers/verify.py +39 -0
  50. procesio_cli/agents/procesio/knowledge.py +115 -0
  51. procesio_cli/agents/procesio/main.py +89 -0
  52. procesio_cli/agents/procesio/tests/__init__.py +0 -0
  53. procesio_cli/agents/procesio/tests/conftest.py +62 -0
  54. procesio_cli/agents/procesio/tests/test_audit.py +91 -0
  55. procesio_cli/agents/procesio/tests/test_checklist.py +28 -0
  56. procesio_cli/agents/procesio/tests/test_guidance.py +78 -0
  57. procesio_cli/agents/procesio/tests/test_manifest_sync.py +48 -0
  58. procesio_cli/agents/procesio/tests/test_verify.py +134 -0
  59. procesio_cli/agents/procesio/tests/test_workspace_scope.py +97 -0
  60. procesio_cli/agents/procesio/verifylib.py +222 -0
  61. procesio_cli/dashboard/README.md +117 -0
  62. procesio_cli/dashboard/__init__.py +18 -0
  63. procesio_cli/dashboard/serve.py +98 -0
  64. procesio_cli/dashboard/server/__init__.py +1 -0
  65. procesio_cli/dashboard/server/config_schemas.py +92 -0
  66. procesio_cli/dashboard/server/google.py +103 -0
  67. procesio_cli/dashboard/server/handler.py +160 -0
  68. procesio_cli/dashboard/server/inventory.py +213 -0
  69. procesio_cli/dashboard/server/jobs.py +118 -0
  70. procesio_cli/dashboard/server/llmtest.py +181 -0
  71. procesio_cli/dashboard/server/procesio_env.py +94 -0
  72. procesio_cli/dashboard/server/routes.py +74 -0
  73. procesio_cli/dashboard/server/runner.py +150 -0
  74. procesio_cli/dashboard/server/security.py +29 -0
  75. procesio_cli/dashboard/server/setup.py +211 -0
  76. procesio_cli/dashboard/server/validate.py +152 -0
  77. procesio_cli/dashboard/tests/conftest.py +16 -0
  78. procesio_cli/dashboard/tests/test_dashboard.py +225 -0
  79. procesio_cli/dashboard/tests/test_procesio_env.py +159 -0
  80. procesio_cli/dashboard/web/actions.js +274 -0
  81. procesio_cli/dashboard/web/api.js +79 -0
  82. procesio_cli/dashboard/web/app.js +385 -0
  83. procesio_cli/dashboard/web/copilot.js +72 -0
  84. procesio_cli/dashboard/web/google.js +121 -0
  85. procesio_cli/dashboard/web/index.html +67 -0
  86. procesio_cli/dashboard/web/procesio_env.js +187 -0
  87. procesio_cli/dashboard/web/styles.css +207 -0
  88. procesio_cli/registry.py +370 -0
  89. procesio_cli/scripts/build-router.py +249 -0
  90. procesio_cli/scripts/build-tool-skill.py +202 -0
  91. procesio_cli/scripts/check-requirements.py +131 -0
  92. procesio_cli/scripts/check-skill-governance.py +174 -0
  93. procesio_cli/scripts/evaluate-skill-routing.py +217 -0
  94. procesio_cli/scripts/evaluate-skills.py +316 -0
  95. procesio_cli/scripts/get-skill.py +122 -0
  96. procesio_cli/scripts/list-skills.py +68 -0
  97. procesio_cli/scripts/list-tools.py +73 -0
  98. procesio_cli/scripts/run-agent.py +59 -0
  99. procesio_cli/scripts/run-tool.py +123 -0
  100. procesio_cli/scripts/secret_scan.py +584 -0
  101. procesio_cli/scripts/set-credential.py +126 -0
  102. procesio_cli/scripts/validate-skills.py +530 -0
  103. procesio_cli/skills/PORTFOLIO.md +134 -0
  104. procesio_cli/skills/__init__.py +6 -0
  105. procesio_cli/skills/agent-skill-engineer/SKILL.md +226 -0
  106. procesio_cli/skills/agent-skill-engineer/evals/evals.json +422 -0
  107. procesio_cli/skills/agent-skill-engineer/references/authoring-standard.md +178 -0
  108. procesio_cli/skills/agent-skill-engineer/references/decision-framework.md +107 -0
  109. procesio_cli/skills/agent-skill-engineer/references/domain-evidence-protocol.md +248 -0
  110. procesio_cli/skills/agent-skill-engineer/references/evaluation-standard.md +191 -0
  111. procesio_cli/skills/agent-skill-engineer/references/expert-lenses.md +167 -0
  112. procesio_cli/skills/agent-skill-engineer/references/field-gate-standard.md +231 -0
  113. procesio_cli/skills/agent-skill-engineer/references/optimization-protocol.md +233 -0
  114. procesio_cli/skills/agent-skill-engineer/references/optimizer-contract.md +268 -0
  115. procesio_cli/skills/agent-skill-engineer/references/portability-standard.md +99 -0
  116. procesio_cli/skills/agent-skill-engineer/references/research-basis.md +128 -0
  117. procesio_cli/skills/agent-skill-engineer/references/review-standard.md +143 -0
  118. procesio_cli/skills/agent-skill-engineer/scripts/audit_skill.py +959 -0
  119. procesio_cli/skills/agent-skill-engineer/scripts/optimize_skill.py +891 -0
  120. procesio_cli/skills/agent-skill-engineer/scripts/scaffold_skill.py +397 -0
  121. procesio_cli/skills/evals/baseline-catalog.json +14 -0
  122. procesio_cli/skills/evals/baseline-routing-v2.json +38 -0
  123. procesio_cli/skills/evals/baseline.json +37 -0
  124. procesio_cli/skills/evals/behavioral.json +367 -0
  125. procesio_cli/skills/evals/field-boundary-development-v1.json +126 -0
  126. procesio_cli/skills/evals/field-closeout-correction-development-v1.json +23 -0
  127. procesio_cli/skills/evals/field-closeout-development-v1.json +89 -0
  128. procesio_cli/skills/evals/portfolio-pressure-training-v1.json +83 -0
  129. procesio_cli/skills/evals/routing.json +363 -0
  130. procesio_cli/skills/evals/validation-baseline.json +4 -0
  131. procesio_cli/skills/procesio-cli/SKILL.md +134 -0
  132. procesio_cli/skills/procesio-cli/evals/evals.json +111 -0
  133. procesio_cli/skills/procesio-cli/evals/workflows.json +16 -0
  134. procesio_cli/skills/procesio-cli/references/connector-lifecycle.md +47 -0
  135. procesio_cli/skills/procesio-cli/references/credentials-admin.md +113 -0
  136. procesio_cli/skills/procesio-cli/references/data-verification.md +71 -0
  137. procesio_cli/skills/procesio-cli/references/documents-files.md +44 -0
  138. procesio_cli/skills/procesio-cli/references/form-e2e.md +57 -0
  139. procesio_cli/skills/procesio-cli/references/operation-contract.md +138 -0
  140. procesio_cli/skills/procesio-cli/references/process-debugging.md +65 -0
  141. procesio_cli/skills/procesio-cli/references/process-lifecycle.md +88 -0
  142. procesio_cli/skills/procesio-cli/references/schedules-webhooks.md +58 -0
  143. procesio_cli/skills/procesio-cli/references/transport-environments.md +53 -0
  144. procesio_cli/skills/procesio-cli-maintainer/SKILL.md +84 -0
  145. procesio_cli/skills/procesio-cli-maintainer/evals/evals.json +116 -0
  146. procesio_cli/skills/procesio-cli-maintainer/references/change-contract.md +34 -0
  147. procesio_cli/skills/procesio-cli-maintainer/references/mcp-compatibility.md +13 -0
  148. procesio_cli/skills/procesio-cli-maintainer/references/skill-authoring.md +51 -0
  149. procesio_cli/skills/procesio-platform-advisor/SKILL.md +82 -0
  150. procesio_cli/skills/procesio-platform-advisor/evals/evals.json +116 -0
  151. procesio_cli/skills/procesio-platform-advisor/references/capability-map.md +22 -0
  152. procesio_cli/skills/procesio-platform-advisor/references/fit-framework.md +35 -0
  153. procesio_cli/skills/procesio-platform-advisor/references/freshness-policy.md +25 -0
  154. procesio_cli/skills/procesio-platform-advisor/references/sizing-method.md +70 -0
  155. procesio_cli/skills/procesio-platform-advisor/scripts/estimate_capacity.py +99 -0
  156. procesio_cli/skills/procesio-setup/SKILL.md +180 -0
  157. procesio_cli/skills/sql-server-optimizer/SKILL.md +86 -0
  158. procesio_cli/skills/sql-server-optimizer/evals/evals.json +116 -0
  159. procesio_cli/skills/sql-server-optimizer/references/db-context.md +44 -0
  160. procesio_cli/skills/sql-server-optimizer/references/generic-mode.md +32 -0
  161. procesio_cli/skills/sql-server-optimizer/references/procesio-mode.md +40 -0
  162. procesio_cli/skills/sql-server-optimizer/references/sql-rules.md +43 -0
  163. procesio_cli/skills/sql-server-optimizer/scripts/export-indexes.sql +178 -0
  164. procesio_cli/skills/sql-server-optimizer/scripts/export-procs-and-functions.sql +118 -0
  165. procesio_cli/skills/sql-server-optimizer/scripts/export-tables.sql +367 -0
  166. procesio_cli/skills/tests/__init__.py +0 -0
  167. procesio_cli/skills/tests/test_agent_skill_engineer.py +240 -0
  168. procesio_cli/skills/tests/test_auth_failure_stop.py +25 -0
  169. procesio_cli/skills/tests/test_capacity_estimator.py +85 -0
  170. procesio_cli/skills/tests/test_field_boundary_learning.py +100 -0
  171. procesio_cli/skills/tests/test_field_closeout_learning.py +123 -0
  172. procesio_cli/skills/tests/test_field_gate_standard.py +76 -0
  173. procesio_cli/skills/tests/test_operational_playbooks.py +51 -0
  174. procesio_cli/skills/tests/test_portfolio_pressure_training.py +35 -0
  175. procesio_cli/skills/tests/test_process_lifecycle_single_run.py +25 -0
  176. procesio_cli/skills/tests/test_skill_evaluator.py +67 -0
  177. procesio_cli/skills/tests/test_skill_optimizer.py +473 -0
  178. procesio_cli/skills/tests/test_skill_portfolio_review.py +70 -0
  179. procesio_cli/skills/tests/test_skill_release_binding.py +204 -0
  180. procesio_cli/skills/tests/test_skill_routing.py +74 -0
  181. procesio_cli/skills/tests/test_skill_scaffold_v2.py +116 -0
  182. procesio_cli/skills/tests/test_validate_skills.py +115 -0
  183. procesio_cli/tools/__init__.py +0 -0
  184. procesio_cli/tools/_lib/README.md +119 -0
  185. procesio_cli/tools/_lib/__init__.py +0 -0
  186. procesio_cli/tools/_lib/accounting.py +120 -0
  187. procesio_cli/tools/_lib/contacts.py +544 -0
  188. procesio_cli/tools/_lib/creds.py +83 -0
  189. procesio_cli/tools/_lib/creds_backends.py +578 -0
  190. procesio_cli/tools/_lib/dbprofiles.py +147 -0
  191. procesio_cli/tools/_lib/dbquery.py +286 -0
  192. procesio_cli/tools/_lib/dbschema.py +225 -0
  193. procesio_cli/tools/_lib/io.py +54 -0
  194. procesio_cli/tools/_lib/manifest.py +297 -0
  195. procesio_cli/tools/_lib/skill_release.py +127 -0
  196. procesio_cli/tools/_lib/skill_resources.py +118 -0
  197. procesio_cli/tools/_lib/userdata.py +228 -0
  198. procesio_cli/tools/_lib/webserialize/__init__.py +16 -0
  199. procesio_cli/tools/_lib/webserialize/admin.py +61 -0
  200. procesio_cli/tools/_lib/webserialize/client.py +264 -0
  201. procesio_cli/tools/_lib/webserialize/config.py +96 -0
  202. procesio_cli/tools/_lib/webserialize/errors.py +27 -0
  203. procesio_cli/tools/_lib/webserialize/framing.py +45 -0
  204. procesio_cli/tools/_lib/webserialize/registry.py +47 -0
  205. procesio_cli/tools/_lib/webserialize/runtime.py +61 -0
  206. procesio_cli/tools/_lib/webserialize/server.py +510 -0
  207. procesio_cli/tools/_lib/websession.py +194 -0
  208. procesio_cli/tools/connector-builder/CONNECTOR-BUILDER-API-NOTES.md +91 -0
  209. procesio_cli/tools/connector-builder/README.md +101 -0
  210. procesio_cli/tools/connector-builder/SKILL.md +248 -0
  211. procesio_cli/tools/connector-builder/__init__.py +7 -0
  212. procesio_cli/tools/connector-builder/actiondef.py +24 -0
  213. procesio_cli/tools/connector-builder/client.py +251 -0
  214. procesio_cli/tools/connector-builder/errors.py +44 -0
  215. procesio_cli/tools/connector-builder/gen_manifest.py +142 -0
  216. procesio_cli/tools/connector-builder/handlers/__init__.py +1 -0
  217. procesio_cli/tools/connector-builder/handlers/admin.py +182 -0
  218. procesio_cli/tools/connector-builder/handlers/auth.py +74 -0
  219. procesio_cli/tools/connector-builder/handlers/builds.py +396 -0
  220. procesio_cli/tools/connector-builder/handlers/common.py +104 -0
  221. procesio_cli/tools/connector-builder/handlers/files.py +111 -0
  222. procesio_cli/tools/connector-builder/handlers/raw.py +38 -0
  223. procesio_cli/tools/connector-builder/handlers/telemetry.py +39 -0
  224. procesio_cli/tools/connector-builder/main.py +94 -0
  225. procesio_cli/tools/connector-builder/tests/conftest.py +146 -0
  226. procesio_cli/tools/connector-builder/tests/test_cb_client.py +86 -0
  227. procesio_cli/tools/connector-builder/tests/test_cb_dispatch.py +105 -0
  228. procesio_cli/tools/connector-builder/tests/test_cb_manifest_sync.py +76 -0
  229. procesio_cli/tools/connector-builder/tests/test_cb_wait.py +123 -0
  230. procesio_cli/tools/connector-builder/tool.yaml +671 -0
  231. procesio_cli/tools/framework-map/.gitattributes +2 -0
  232. procesio_cli/tools/framework-map/README.md +98 -0
  233. procesio_cli/tools/framework-map/SKILL.md +30 -0
  234. procesio_cli/tools/framework-map/app.js +188 -0
  235. procesio_cli/tools/framework-map/fm_builder.py +476 -0
  236. procesio_cli/tools/framework-map/fm_catalog.py +19 -0
  237. procesio_cli/tools/framework-map/fm_ro_catalog.py +94 -0
  238. procesio_cli/tools/framework-map/fm_strings.py +101 -0
  239. procesio_cli/tools/framework-map/main.py +95 -0
  240. procesio_cli/tools/framework-map/styles.css +214 -0
  241. procesio_cli/tools/framework-map/tests/conftest.py +33 -0
  242. procesio_cli/tools/framework-map/tests/test_fm_build.py +126 -0
  243. procesio_cli/tools/framework-map/tests/test_fm_manifest_sync.py +63 -0
  244. procesio_cli/tools/framework-map/tests/test_fm_store_tree.py +75 -0
  245. procesio_cli/tools/framework-map/tool.yaml +61 -0
  246. procesio_cli/tools/mysql/MOVED-TO-CENTRAL.md +29 -0
  247. procesio_cli/tools/mysql/README.md +196 -0
  248. procesio_cli/tools/mysql/SKILL.md +37 -0
  249. procesio_cli/tools/mysql/__init__.py +0 -0
  250. procesio_cli/tools/mysql/connection.py +191 -0
  251. procesio_cli/tools/mysql/main.py +487 -0
  252. procesio_cli/tools/mysql/profiles.template.json +1 -0
  253. procesio_cli/tools/mysql/schema.py +434 -0
  254. procesio_cli/tools/mysql/tests/__init__.py +0 -0
  255. procesio_cli/tools/mysql/tests/conftest.py +21 -0
  256. procesio_cli/tools/mysql/tests/test_manifest_sync.py +68 -0
  257. procesio_cli/tools/mysql/tests/test_mysql.py +201 -0
  258. procesio_cli/tools/mysql/tests/test_mysql_schema.py +337 -0
  259. procesio_cli/tools/mysql/tool.yaml +112 -0
  260. procesio_cli/tools/procesio/DTO-SUBTOOLS-NOTE.md +360 -0
  261. procesio_cli/tools/procesio/FORM-DEV-GUIDE/00-INDEX.md +67 -0
  262. procesio_cli/tools/procesio/FORM-DEV-GUIDE/01-ANATOMY.md +209 -0
  263. procesio_cli/tools/procesio/FORM-DEV-GUIDE/02-CODE-INJECTION.md +309 -0
  264. procesio_cli/tools/procesio/FORM-DEV-GUIDE/03-DOM-CONTRACT.md +370 -0
  265. procesio_cli/tools/procesio/FORM-DEV-GUIDE/04-INTERACTION-RECIPES.md +600 -0
  266. procesio_cli/tools/procesio/FORM-DEV-GUIDE/05-STEPPER-AND-MOTION.md +426 -0
  267. procesio_cli/tools/procesio/FORM-DEV-GUIDE/06-PROCESS-INTEGRATION.md +347 -0
  268. procesio_cli/tools/procesio/FORM-DEV-GUIDE/07-DEPLOY-WORKFLOW.md +294 -0
  269. procesio_cli/tools/procesio/FORM-DEV-GUIDE/08-PITFALLS.md +569 -0
  270. procesio_cli/tools/procesio/FORM-DEV-GUIDE/09-NAVIGATION-AND-HOSTING.md +308 -0
  271. procesio_cli/tools/procesio/PHASE4-E2E-NOTES.md +594 -0
  272. procesio_cli/tools/procesio/PROCESIO-API-CORRECTIONS.md +295 -0
  273. procesio_cli/tools/procesio/PROCESIO-API-NOTES.md +6976 -0
  274. procesio_cli/tools/procesio/PROCESIO-AUTH-NOTES.md +226 -0
  275. procesio_cli/tools/procesio/PROCESIO-CARD-BUILD-NOTES.md +183 -0
  276. procesio_cli/tools/procesio/PROCESIO-CONNECTORS-NOTES.md +141 -0
  277. procesio_cli/tools/procesio/PROCESIO-CUSTOM-ACTION-NOTES.md +179 -0
  278. procesio_cli/tools/procesio/PROCESIO-DOCS-FIX-REPORT.md +123 -0
  279. procesio_cli/tools/procesio/PROCESIO-ENVIRONMENTS-NOTES.md +94 -0
  280. procesio_cli/tools/procesio/PROCESIO-FE-VALIDATION-NOTES.md +107 -0
  281. procesio_cli/tools/procesio/PROCESIO-FOREACH-NOTES.md +199 -0
  282. procesio_cli/tools/procesio/PROCESIO-FORM-API-HANG-NOTE.md +101 -0
  283. procesio_cli/tools/procesio/PROCESIO-FORM-SUBMISSION-NOTES.md +172 -0
  284. procesio_cli/tools/procesio/PROCESIO-HTML-TO-DOCX-NOTES.md +110 -0
  285. procesio_cli/tools/procesio/PROCESIO-INSTANCE-HISTORY-NOTES.md +242 -0
  286. procesio_cli/tools/procesio/PROCESIO-METERING-NOTES.md +201 -0
  287. procesio_cli/tools/procesio/PROCESIO-NODE-CODE-NOTES.md +279 -0
  288. procesio_cli/tools/procesio/PROCESIO-NODE-MODULE-WHITELIST.md +113 -0
  289. procesio_cli/tools/procesio/PROCESIO-PYTHON-ACTION-NOTES.md +165 -0
  290. procesio_cli/tools/procesio/PROCESIO-QUERY-STORE-NOTES.md +826 -0
  291. procesio_cli/tools/procesio/PROCESIO-RECONCILIATION-PATTERNS.md +104 -0
  292. procesio_cli/tools/procesio/PROCESIO-RESOURCE-MODEL-NOTES.md +1100 -0
  293. procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-CRED-OPTIONALITY.md +45 -0
  294. procesio_cli/tools/procesio/PROCESIO-SEND-EMAIL-NOTES.md +252 -0
  295. procesio_cli/tools/procesio/PROCESIO-SQL-ACTIONS-NOTES.md +284 -0
  296. procesio_cli/tools/procesio/PROCESIO-USAGE-GUIDE.md +219 -0
  297. procesio_cli/tools/procesio/README.md +341 -0
  298. procesio_cli/tools/procesio/SCHEDULE-INPUT-SECURITY-NOTES.md +63 -0
  299. procesio_cli/tools/procesio/SKILL.md +694 -0
  300. procesio_cli/tools/procesio/__init__.py +1 -0
  301. procesio_cli/tools/procesio/actiondef.py +27 -0
  302. procesio_cli/tools/procesio/assets/html2docx/html2docx.js +2155 -0
  303. procesio_cli/tools/procesio/auth.py +354 -0
  304. procesio_cli/tools/procesio/autogen.py +121 -0
  305. procesio_cli/tools/procesio/client.py +355 -0
  306. procesio_cli/tools/procesio/config.py +96 -0
  307. procesio_cli/tools/procesio/data/endpoints.json +2901 -0
  308. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/01-authentication.md +180 -0
  309. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/02-conventions.md +116 -0
  310. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/ANONYMOUS-SURFACE.md +51 -0
  311. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/README.md +104 -0
  312. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/SYMBOL-INDEX.md +444 -0
  313. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/03-auth-users-workspaces.md +731 -0
  314. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/04-processes.md +913 -0
  315. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/05-actions.md +605 -0
  316. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/06-files.md +237 -0
  317. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/07-forms-documents.md +985 -0
  318. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/08-credentials.md +532 -0
  319. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/09-datastore-datatypes.md +830 -0
  320. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/10-custom-urls.md +319 -0
  321. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/11-subscriptions-resources-analytics.md +455 -0
  322. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/12-webhooks-notifications-misc.md +519 -0
  323. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/13-realtime-signalr.md +153 -0
  324. procesio_cli/tools/procesio/docs_info/API-DOCUMENTATION/endpoints/99-internal-and-other-services.md +52 -0
  325. procesio_cli/tools/procesio/docs_info/FormBuilder mock.ts +5725 -0
  326. procesio_cli/tools/procesio/docs_info/Procesio Platform Actions.json +30219 -0
  327. procesio_cli/tools/procesio/docs_info/form-config-enums.ts +92 -0
  328. procesio_cli/tools/procesio/docs_info/form-theme.ts +1603 -0
  329. procesio_cli/tools/procesio/docs_info/process-validation-reference-1.md +2739 -0
  330. procesio_cli/tools/procesio/dto/__init__.py +13 -0
  331. procesio_cli/tools/procesio/dto/credential/builder.py +179 -0
  332. procesio_cli/tools/procesio/dto/credential/config.schema.json +19 -0
  333. procesio_cli/tools/procesio/dto/credential/description.md +193 -0
  334. procesio_cli/tools/procesio/dto/credential/fixtures/rest.config.json +11 -0
  335. procesio_cli/tools/procesio/dto/credential/fixtures/rest.dto.json +28 -0
  336. procesio_cli/tools/procesio/dto/credential/fixtures/rest.template.json +44 -0
  337. procesio_cli/tools/procesio/dto/credential/template.dto.json +16 -0
  338. procesio_cli/tools/procesio/dto/data/_refresh.py +147 -0
  339. procesio_cli/tools/procesio/dto/data/action_catalog.json +30825 -0
  340. procesio_cli/tools/procesio/dto/data/action_families.json +55 -0
  341. procesio_cli/tools/procesio/dto/data/platform_types.json +182 -0
  342. procesio_cli/tools/procesio/dto/datatype/builder.py +223 -0
  343. procesio_cli/tools/procesio/dto/datatype/config.schema.json +47 -0
  344. procesio_cli/tools/procesio/dto/datatype/description.md +85 -0
  345. procesio_cli/tools/procesio/dto/datatype/fixtures/customer.config.json +41 -0
  346. procesio_cli/tools/procesio/dto/datatype/fixtures/customer.dto.json +164 -0
  347. procesio_cli/tools/procesio/dto/datatype/template.dto.json +37 -0
  348. procesio_cli/tools/procesio/dto/document/builder.py +193 -0
  349. procesio_cli/tools/procesio/dto/document/config.schema.json +33 -0
  350. procesio_cli/tools/procesio/dto/document/description.md +124 -0
  351. procesio_cli/tools/procesio/dto/document/fixtures/invoice.config.json +16 -0
  352. procesio_cli/tools/procesio/dto/document/fixtures/invoice.dto.json +32 -0
  353. procesio_cli/tools/procesio/dto/document/template.dto.json +14 -0
  354. procesio_cli/tools/procesio/dto/form/FORM-STYLING-NOTES.md +142 -0
  355. procesio_cli/tools/procesio/dto/form/GOLDEN-DRIFT-NOTES.md +72 -0
  356. procesio_cli/tools/procesio/dto/form/RUN-PROCESS-RESULT-NOTES.md +80 -0
  357. procesio_cli/tools/procesio/dto/form/addelement.py +277 -0
  358. procesio_cli/tools/procesio/dto/form/builder.py +1009 -0
  359. procesio_cli/tools/procesio/dto/form/capture_goldens.py +106 -0
  360. procesio_cli/tools/procesio/dto/form/code_cipher.py +67 -0
  361. procesio_cli/tools/procesio/dto/form/config.schema.json +76 -0
  362. procesio_cli/tools/procesio/dto/form/data_shell.json +1 -0
  363. procesio_cli/tools/procesio/dto/form/description.md +346 -0
  364. procesio_cli/tools/procesio/dto/form/elements/approval.json +259 -0
  365. procesio_cli/tools/procesio/dto/form/elements/assignee.json +137 -0
  366. procesio_cli/tools/procesio/dto/form/elements/button.json +125 -0
  367. procesio_cli/tools/procesio/dto/form/elements/chart.json +341 -0
  368. procesio_cli/tools/procesio/dto/form/elements/chat.json +199 -0
  369. procesio_cli/tools/procesio/dto/form/elements/checkbox.json +153 -0
  370. procesio_cli/tools/procesio/dto/form/elements/column.json +77 -0
  371. procesio_cli/tools/procesio/dto/form/elements/columns.json +78 -0
  372. procesio_cli/tools/procesio/dto/form/elements/datetime-input.json +304 -0
  373. procesio_cli/tools/procesio/dto/form/elements/divider.json +54 -0
  374. procesio_cli/tools/procesio/dto/form/elements/dropdown.json +130 -0
  375. procesio_cli/tools/procesio/dto/form/elements/dynamic-table-row.json +253 -0
  376. procesio_cli/tools/procesio/dto/form/elements/file-upload.json +196 -0
  377. procesio_cli/tools/procesio/dto/form/elements/file-viewer.json +169 -0
  378. procesio_cli/tools/procesio/dto/form/elements/heading.json +79 -0
  379. procesio_cli/tools/procesio/dto/form/elements/icon.json +107 -0
  380. procesio_cli/tools/procesio/dto/form/elements/image.json +174 -0
  381. procesio_cli/tools/procesio/dto/form/elements/input.json +206 -0
  382. procesio_cli/tools/procesio/dto/form/elements/list.json +202 -0
  383. procesio_cli/tools/procesio/dto/form/elements/number-input.json +202 -0
  384. procesio_cli/tools/procesio/dto/form/elements/paragraph.json +79 -0
  385. procesio_cli/tools/procesio/dto/form/elements/radiobox.json +174 -0
  386. procesio_cli/tools/procesio/dto/form/elements/section.json +114 -0
  387. procesio_cli/tools/procesio/dto/form/elements/select.json +217 -0
  388. procesio_cli/tools/procesio/dto/form/elements/side-panel.json +146 -0
  389. procesio_cli/tools/procesio/dto/form/elements/signature-pad.json +141 -0
  390. procesio_cli/tools/procesio/dto/form/elements/static-table-row.json +84 -0
  391. procesio_cli/tools/procesio/dto/form/elements/step.json +105 -0
  392. procesio_cli/tools/procesio/dto/form/elements/stepper.json +126 -0
  393. procesio_cli/tools/procesio/dto/form/elements/tab.json +77 -0
  394. procesio_cli/tools/procesio/dto/form/elements/table.json +279 -0
  395. procesio_cli/tools/procesio/dto/form/elements/tabs.json +114 -0
  396. procesio_cli/tools/procesio/dto/form/elements/textarea.json +213 -0
  397. procesio_cli/tools/procesio/dto/form/enum_map.json +68 -0
  398. procesio_cli/tools/procesio/dto/form/fieldpath.py +284 -0
  399. procesio_cli/tools/procesio/dto/form/fixtures/reg.config.json +32 -0
  400. procesio_cli/tools/procesio/dto/form/fixtures/reg.elements.json +638 -0
  401. procesio_cli/tools/procesio/dto/form/sync_from_mock.py +386 -0
  402. procesio_cli/tools/procesio/dto/framework.py +125 -0
  403. procesio_cli/tools/procesio/dto/process/builder.py +1991 -0
  404. procesio_cli/tools/procesio/dto/process/config.schema.json +159 -0
  405. procesio_cli/tools/procesio/dto/process/description.md +218 -0
  406. procesio_cli/tools/procesio/dto/process/fixtures/greet.config.json +32 -0
  407. procesio_cli/tools/procesio/dto/process/fixtures/greet.dto.json +317 -0
  408. procesio_cli/tools/procesio/dto/process/naming.py +305 -0
  409. procesio_cli/tools/procesio/dto/process/normalize.py +74 -0
  410. procesio_cli/tools/procesio/dto/process/template.dto.json +32 -0
  411. procesio_cli/tools/procesio/dto/refdata.py +87 -0
  412. procesio_cli/tools/procesio/dto/registry.py +27 -0
  413. procesio_cli/tools/procesio/dto/webhook/builder.py +142 -0
  414. procesio_cli/tools/procesio/dto/webhook/config.schema.json +25 -0
  415. procesio_cli/tools/procesio/dto/webhook/description.md +92 -0
  416. procesio_cli/tools/procesio/dto/webhook/fixtures/order.config.json +10 -0
  417. procesio_cli/tools/procesio/dto/webhook/fixtures/order.dto.json +25 -0
  418. procesio_cli/tools/procesio/dto/webhook/template.dto.json +14 -0
  419. procesio_cli/tools/procesio/environments.py +264 -0
  420. procesio_cli/tools/procesio/errors.py +166 -0
  421. procesio_cli/tools/procesio/flowmodel/README.md +37 -0
  422. procesio_cli/tools/procesio/flowmodel/__init__.py +14 -0
  423. procesio_cli/tools/procesio/flowmodel/digest.py +223 -0
  424. procesio_cli/tools/procesio/flowmodel/families.py +34 -0
  425. procesio_cli/tools/procesio/flowmodel/fevalidation.py +1098 -0
  426. procesio_cli/tools/procesio/flowmodel/formdigest.py +232 -0
  427. procesio_cli/tools/procesio/flowmodel/inspect.py +78 -0
  428. procesio_cli/tools/procesio/flowmodel/model.py +82 -0
  429. procesio_cli/tools/procesio/flowmodel/nodeparam.py +871 -0
  430. procesio_cli/tools/procesio/flowmodel/reader.py +191 -0
  431. procesio_cli/tools/procesio/flowmodel/sqlparam.py +410 -0
  432. procesio_cli/tools/procesio/form_code_keys.py +50 -0
  433. procesio_cli/tools/procesio/formlint.py +292 -0
  434. procesio_cli/tools/procesio/gen_manifest.py +118 -0
  435. procesio_cli/tools/procesio/handlers/__init__.py +1 -0
  436. procesio_cli/tools/procesio/handlers/auth_actions.py +103 -0
  437. procesio_cli/tools/procesio/handlers/catalog.py +147 -0
  438. procesio_cli/tools/procesio/handlers/common.py +90 -0
  439. procesio_cli/tools/procesio/handlers/customaction_ops.py +146 -0
  440. procesio_cli/tools/procesio/handlers/datastore_ops.py +482 -0
  441. procesio_cli/tools/procesio/handlers/datatype_ops.py +275 -0
  442. procesio_cli/tools/procesio/handlers/dto_actions.py +258 -0
  443. procesio_cli/tools/procesio/handlers/environment_admin.py +146 -0
  444. procesio_cli/tools/procesio/handlers/fevalidate.py +236 -0
  445. procesio_cli/tools/procesio/handlers/files.py +141 -0
  446. procesio_cli/tools/procesio/handlers/flowgraph.py +229 -0
  447. procesio_cli/tools/procesio/handlers/flowlint.py +269 -0
  448. procesio_cli/tools/procesio/handlers/form_chains.py +336 -0
  449. procesio_cli/tools/procesio/handlers/form_code.py +277 -0
  450. procesio_cli/tools/procesio/handlers/form_element.py +199 -0
  451. procesio_cli/tools/procesio/handlers/form_events.py +441 -0
  452. procesio_cli/tools/procesio/handlers/form_update.py +270 -0
  453. procesio_cli/tools/procesio/handlers/formprocess.py +211 -0
  454. procesio_cli/tools/procesio/handlers/generic.py +135 -0
  455. procesio_cli/tools/procesio/handlers/layout.py +137 -0
  456. procesio_cli/tools/procesio/handlers/nodeparams.py +1018 -0
  457. procesio_cli/tools/procesio/handlers/process_layout.py +189 -0
  458. procesio_cli/tools/procesio/handlers/process_naming.py +213 -0
  459. procesio_cli/tools/procesio/handlers/processes.py +416 -0
  460. procesio_cli/tools/procesio/handlers/profile_admin.py +184 -0
  461. procesio_cli/tools/procesio/handlers/resource_ops.py +447 -0
  462. procesio_cli/tools/procesio/handlers/schedules.py +320 -0
  463. procesio_cli/tools/procesio/handlers/sqlactions.py +232 -0
  464. procesio_cli/tools/procesio/handlers/transport.py +463 -0
  465. procesio_cli/tools/procesio/handlers/usageguide.py +222 -0
  466. procesio_cli/tools/procesio/layout/README.md +196 -0
  467. procesio_cli/tools/procesio/layout/__init__.py +11 -0
  468. procesio_cli/tools/procesio/layout/adapter.py +736 -0
  469. procesio_cli/tools/procesio/layout/cycles.py +186 -0
  470. procesio_cli/tools/procesio/layout/dispatch.py +39 -0
  471. procesio_cli/tools/procesio/layout/elk/elk_runner.mjs +50 -0
  472. procesio_cli/tools/procesio/layout/elk/package-lock.json +21 -0
  473. procesio_cli/tools/procesio/layout/elk/package.json +11 -0
  474. procesio_cli/tools/procesio/layout/elk_engine.py +568 -0
  475. procesio_cli/tools/procesio/layout/engine.py +1311 -0
  476. procesio_cli/tools/procesio/layout/report.py +187 -0
  477. procesio_cli/tools/procesio/main.py +179 -0
  478. procesio_cli/tools/procesio/profiles.py +193 -0
  479. procesio_cli/tools/procesio/reliability.py +193 -0
  480. procesio_cli/tools/procesio/sqlformat.py +167 -0
  481. procesio_cli/tools/procesio/swagger.py +51 -0
  482. procesio_cli/tools/procesio/tests/__init__.py +0 -0
  483. procesio_cli/tools/procesio/tests/conftest.py +129 -0
  484. procesio_cli/tools/procesio/tests/test_auth.py +200 -0
  485. procesio_cli/tools/procesio/tests/test_autogen.py +78 -0
  486. procesio_cli/tools/procesio/tests/test_builder_attrpath_sqlbind.py +180 -0
  487. procesio_cli/tools/procesio/tests/test_builder_custom_action_catalog.py +72 -0
  488. procesio_cli/tools/procesio/tests/test_client.py +114 -0
  489. procesio_cli/tools/procesio/tests/test_client_reliability.py +139 -0
  490. procesio_cli/tools/procesio/tests/test_customaction.py +103 -0
  491. procesio_cli/tools/procesio/tests/test_customaction_test.py +80 -0
  492. procesio_cli/tools/procesio/tests/test_cycles.py +69 -0
  493. procesio_cli/tools/procesio/tests/test_datastore.py +378 -0
  494. procesio_cli/tools/procesio/tests/test_datastore_node.py +94 -0
  495. procesio_cli/tools/procesio/tests/test_dispatch.py +170 -0
  496. procesio_cli/tools/procesio/tests/test_document_page_size.py +19 -0
  497. procesio_cli/tools/procesio/tests/test_dto.py +734 -0
  498. procesio_cli/tools/procesio/tests/test_edit_dryrun_preview.py +96 -0
  499. procesio_cli/tools/procesio/tests/test_elk_layout.py +221 -0
  500. procesio_cli/tools/procesio/tests/test_environments.py +214 -0
  501. procesio_cli/tools/procesio/tests/test_fevalidate_gate.py +104 -0
  502. procesio_cli/tools/procesio/tests/test_fevalidation.py +317 -0
  503. procesio_cli/tools/procesio/tests/test_fieldpath_variables.py +87 -0
  504. procesio_cli/tools/procesio/tests/test_files.py +72 -0
  505. procesio_cli/tools/procesio/tests/test_flow_digest.py +202 -0
  506. procesio_cli/tools/procesio/tests/test_flowlint.py +114 -0
  507. procesio_cli/tools/procesio/tests/test_flowmodel.py +158 -0
  508. procesio_cli/tools/procesio/tests/test_form_add_element.py +256 -0
  509. procesio_cli/tools/procesio/tests/test_form_approval_gaps.py +319 -0
  510. procesio_cli/tools/procesio/tests/test_form_auto_names.py +60 -0
  511. procesio_cli/tools/procesio/tests/test_form_chains.py +217 -0
  512. procesio_cli/tools/procesio/tests/test_form_code_actions.py +188 -0
  513. procesio_cli/tools/procesio/tests/test_form_code_keys.py +92 -0
  514. procesio_cli/tools/procesio/tests/test_form_code_style.py +178 -0
  515. procesio_cli/tools/procesio/tests/test_form_delete_element.py +99 -0
  516. procesio_cli/tools/procesio/tests/test_form_element_config.py +141 -0
  517. procesio_cli/tools/procesio/tests/test_form_event_maps.py +135 -0
  518. procesio_cli/tools/procesio/tests/test_form_event_ops.py +376 -0
  519. procesio_cli/tools/procesio/tests/test_form_event_replace.py +86 -0
  520. procesio_cli/tools/procesio/tests/test_form_events.py +92 -0
  521. procesio_cli/tools/procesio/tests/test_form_parity.py +171 -0
  522. procesio_cli/tools/procesio/tests/test_form_select_source.py +74 -0
  523. procesio_cli/tools/procesio/tests/test_form_sync.py +35 -0
  524. procesio_cli/tools/procesio/tests/test_form_update.py +133 -0
  525. procesio_cli/tools/procesio/tests/test_formlint.py +155 -0
  526. procesio_cli/tools/procesio/tests/test_formlint_css_scope.py +38 -0
  527. procesio_cli/tools/procesio/tests/test_formlint_field_names.py +83 -0
  528. procesio_cli/tools/procesio/tests/test_guards.py +110 -0
  529. procesio_cli/tools/procesio/tests/test_hidden_setting_types.py +39 -0
  530. procesio_cli/tools/procesio/tests/test_html2docx_asset.py +132 -0
  531. procesio_cli/tools/procesio/tests/test_key_handle_terminology.py +45 -0
  532. procesio_cli/tools/procesio/tests/test_layout.py +574 -0
  533. procesio_cli/tools/procesio/tests/test_layout_report.py +88 -0
  534. procesio_cli/tools/procesio/tests/test_manifest_sync.py +68 -0
  535. procesio_cli/tools/procesio/tests/test_nodedelete.py +151 -0
  536. procesio_cli/tools/procesio/tests/test_nodeparam.py +826 -0
  537. procesio_cli/tools/procesio/tests/test_normalize.py +70 -0
  538. procesio_cli/tools/procesio/tests/test_payload_docs.py +86 -0
  539. procesio_cli/tools/procesio/tests/test_process_action_naming.py +170 -0
  540. procesio_cli/tools/procesio/tests/test_process_call_api_seed.py +46 -0
  541. procesio_cli/tools/procesio/tests/test_process_ext.py +373 -0
  542. procesio_cli/tools/procesio/tests/test_process_invariants.py +183 -0
  543. procesio_cli/tools/procesio/tests/test_process_layout.py +117 -0
  544. procesio_cli/tools/procesio/tests/test_process_naming.py +188 -0
  545. procesio_cli/tools/procesio/tests/test_process_query_store.py +172 -0
  546. procesio_cli/tools/procesio/tests/test_processinfo_variable.py +56 -0
  547. procesio_cli/tools/procesio/tests/test_profiles.py +127 -0
  548. procesio_cli/tools/procesio/tests/test_put_projects.py +41 -0
  549. procesio_cli/tools/procesio/tests/test_reliability.py +107 -0
  550. procesio_cli/tools/procesio/tests/test_removal_gate.py +95 -0
  551. procesio_cli/tools/procesio/tests/test_resource_ops.py +173 -0
  552. procesio_cli/tools/procesio/tests/test_save_validity.py +207 -0
  553. procesio_cli/tools/procesio/tests/test_schedule_cron_shape.py +111 -0
  554. procesio_cli/tools/procesio/tests/test_schedules.py +309 -0
  555. procesio_cli/tools/procesio/tests/test_sqlconvert.py +234 -0
  556. procesio_cli/tools/procesio/tests/test_sqlformat.py +83 -0
  557. procesio_cli/tools/procesio/tests/test_sqlparam.py +187 -0
  558. procesio_cli/tools/procesio/tests/test_superseded_actions.py +54 -0
  559. procesio_cli/tools/procesio/tests/test_surgical_concurrency.py +156 -0
  560. procesio_cli/tools/procesio/tests/test_tier4.py +123 -0
  561. procesio_cli/tools/procesio/tests/test_transport.py +147 -0
  562. procesio_cli/tools/procesio/tests/test_transport_data_stores.py +168 -0
  563. procesio_cli/tools/procesio/tests/test_transport_mapper_repair.py +150 -0
  564. procesio_cli/tools/procesio/tests/test_transport_webhook_strip.py +253 -0
  565. procesio_cli/tools/procesio/tests/test_usage_guide.py +96 -0
  566. procesio_cli/tools/procesio/tool.yaml +12486 -0
  567. procesio_cli/tools/sqlserver/MOVED-TO-CENTRAL.md +29 -0
  568. procesio_cli/tools/sqlserver/README.md +192 -0
  569. procesio_cli/tools/sqlserver/SKILL.md +37 -0
  570. procesio_cli/tools/sqlserver/SQLSERVER-SCHEMA-NOTES.md +284 -0
  571. procesio_cli/tools/sqlserver/__init__.py +0 -0
  572. procesio_cli/tools/sqlserver/connection.py +242 -0
  573. procesio_cli/tools/sqlserver/main.py +521 -0
  574. procesio_cli/tools/sqlserver/profiles.template.json +1 -0
  575. procesio_cli/tools/sqlserver/schema.py +600 -0
  576. procesio_cli/tools/sqlserver/tests/__init__.py +0 -0
  577. procesio_cli/tools/sqlserver/tests/conftest.py +21 -0
  578. procesio_cli/tools/sqlserver/tests/test_connection.py +75 -0
  579. procesio_cli/tools/sqlserver/tests/test_dbprofiles.py +92 -0
  580. procesio_cli/tools/sqlserver/tests/test_dbquery.py +220 -0
  581. procesio_cli/tools/sqlserver/tests/test_manifest_sync.py +68 -0
  582. procesio_cli/tools/sqlserver/tests/test_sqlserver.py +326 -0
  583. procesio_cli/tools/sqlserver/tests/test_sqlserver_schema.py +488 -0
  584. procesio_cli/tools/sqlserver/tool.yaml +123 -0
  585. procesio_cli/tools/web/README.md +381 -0
  586. procesio_cli/tools/web/SKILL.md +37 -0
  587. procesio_cli/tools/web/WEB-AUTOMATION-NOTES.md +277 -0
  588. procesio_cli/tools/web/__init__.py +21 -0
  589. procesio_cli/tools/web/actiondef.py +24 -0
  590. procesio_cli/tools/web/api.py +444 -0
  591. procesio_cli/tools/web/brokered.py +107 -0
  592. procesio_cli/tools/web/driver.py +839 -0
  593. procesio_cli/tools/web/drivercontract.py +44 -0
  594. procesio_cli/tools/web/errors.py +54 -0
  595. procesio_cli/tools/web/handlers/__init__.py +1 -0
  596. procesio_cli/tools/web/handlers/archive.py +367 -0
  597. procesio_cli/tools/web/handlers/browse.py +182 -0
  598. procesio_cli/tools/web/handlers/contacts.py +74 -0
  599. procesio_cli/tools/web/handlers/sessmgmt.py +120 -0
  600. procesio_cli/tools/web/main.py +128 -0
  601. procesio_cli/tools/web/sessions.py +253 -0
  602. procesio_cli/tools/web/steps.py +162 -0
  603. procesio_cli/tools/web/tests/__init__.py +0 -0
  604. procesio_cli/tools/web/tests/conftest.py +197 -0
  605. procesio_cli/tools/web/tests/test_api.py +330 -0
  606. procesio_cli/tools/web/tests/test_archive.py +190 -0
  607. procesio_cli/tools/web/tests/test_brokered.py +138 -0
  608. procesio_cli/tools/web/tests/test_dispatch.py +132 -0
  609. procesio_cli/tools/web/tests/test_errors.py +60 -0
  610. procesio_cli/tools/web/tests/test_manifest_sync.py +76 -0
  611. procesio_cli/tools/web/tests/test_page_contacts.py +68 -0
  612. procesio_cli/tools/web/tests/test_sessions.py +145 -0
  613. procesio_cli/tools/web/tests/test_steps.py +210 -0
  614. procesio_cli/tools/web/tool.yaml +184 -0
  615. procesio_cli/tools/xlsx/README.md +34 -0
  616. procesio_cli/tools/xlsx/SKILL.md +28 -0
  617. procesio_cli/tools/xlsx/__init__.py +0 -0
  618. procesio_cli/tools/xlsx/main.py +129 -0
  619. procesio_cli/tools/xlsx/reader.py +91 -0
  620. procesio_cli/tools/xlsx/tests/__init__.py +0 -0
  621. procesio_cli/tools/xlsx/tests/conftest.py +39 -0
  622. procesio_cli/tools/xlsx/tests/test_xlsx.py +103 -0
  623. procesio_cli/tools/xlsx/tool.yaml +30 -0
  624. procesio_cli/webplatform/aat_mcp/AAT-MCP-NOTES.md +244 -0
  625. procesio_cli/webplatform/aat_mcp/README.md +78 -0
  626. procesio_cli/webplatform/aat_mcp/bridge.py +705 -0
  627. procesio_cli/webplatform/aat_mcp/chat_server.py +194 -0
  628. procesio_cli/webplatform/aat_mcp/chat_surface.snapshot.json +668 -0
  629. procesio_cli/webplatform/aat_mcp/chat_surface.yaml +372 -0
  630. procesio_cli/webplatform/aat_mcp/gate.py +68 -0
  631. procesio_cli/webplatform/aat_mcp/http_server.py +139 -0
  632. procesio_cli/webplatform/aat_mcp/protocol.py +175 -0
  633. procesio_cli/webplatform/aat_mcp/server.py +306 -0
  634. procesio_cli/webplatform/aat_mcp/tests/conftest.py +8 -0
  635. procesio_cli/webplatform/aat_mcp/tests/test_aat_mcp.py +255 -0
  636. procesio_cli/webplatform/aat_mcp/tests/test_chat_surface.py +247 -0
  637. procesio_cli/webplatform/aat_mcp/tests/test_discovery.py +489 -0
  638. procesio_cli/webplatform/aat_mcp/tests/test_multiuser.py +86 -0
  639. procesio_cli/webplatform/aat_mcp/tests/test_progressive_discovery.py +89 -0
  640. procesio_cli/webplatform/aat_mcp/tests/test_protocol.py +180 -0
  641. procesio_cli/webplatform/aat_mcp/tests/test_skill_discovery.py +23 -0
  642. procesio_cli-0.1.1.dist-info/METADATA +467 -0
  643. procesio_cli-0.1.1.dist-info/RECORD +647 -0
  644. procesio_cli-0.1.1.dist-info/WHEEL +4 -0
  645. procesio_cli-0.1.1.dist-info/entry_points.txt +7 -0
  646. procesio_cli-0.1.1.dist-info/licenses/LICENSE +201 -0
  647. procesio_cli-0.1.1.dist-info/licenses/NOTICE +5 -0
@@ -0,0 +1,871 @@
1
+ """Surgical read/write of ONE runtime parameter on ONE node of a PROCESIO flow.
2
+
3
+ Why this exists: the desired-state builders (`process-create` / `process-edit`) rebuild a whole flow
4
+ from a config. That is the right tool when you own the config, and the wrong one when a live flow was
5
+ hand-built in the designer and a single literal has to change — an API endpoint that moved host, a
6
+ timeout, a SQL statement, a script body. Rebuilding such a flow from a reconstructed config risks
7
+ losing everything the config cannot express; changing one `parameters[]` value risks nothing.
8
+
9
+ Model (see PROCESIO-API-NOTES.md):
10
+ - a node = `flow['actions'][i]`; its RUNTIME layer is `parameters[]`, each entry
11
+ `{tabPropertyId, value, variable[]}`. `value` is the literal text with `<%N%>` placeholders;
12
+ `variable[]` binds each `N` to a `{id, variableId, attribute}`.
13
+ - the DESIGNER layer (`customData.configuration[].settings[]`) mirrors it, keyed by the SAME id as
14
+ `tabPropertyId`, except a variable ref is the raw variable GUID there. NEVER hand-write it —
15
+ `dto.process.normalize.normalize_designer_layer` regenerates it from the runtime layer.
16
+
17
+ Scope guard: only a STRING parameter value may be set here. List/dict-shaped values (request-payload
18
+ tabs, decisional cases, subprocess maps, document mappers) are structured DTOs whose designer mirror is
19
+ not a clean 1:1 — those belong to the builder, not to a text patch.
20
+
21
+ Pure: mutates a raw flow DTO dict in place; the caller fetches / normalizes / validates / PUTs.
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import re
26
+
27
+ _PLACEHOLDER = re.compile(r"<%(\d+)%>")
28
+
29
+
30
+ def find_node(flow: dict, key: str) -> dict | None:
31
+ """A node by id or by its canvas label (`actionName`). Exact match, id first."""
32
+ actions = flow.get("actions") or []
33
+ for a in actions:
34
+ if a.get("id") == key:
35
+ return a
36
+ for a in actions:
37
+ if a.get("actionName") == key:
38
+ return a
39
+ return None
40
+
41
+
42
+ def _settings_index(node: dict) -> dict:
43
+ """{setting.id: setting} for a node's designer settings, recursing into side-pannel nesting."""
44
+ byid: dict = {}
45
+
46
+ def walk(settings):
47
+ for s in settings or []:
48
+ if isinstance(s, dict):
49
+ if s.get("id"):
50
+ byid[s["id"]] = s
51
+ if isinstance(s.get("value"), list):
52
+ walk(s["value"])
53
+ for tab in (node.get("customData") or {}).get("configuration") or []:
54
+ walk(tab.get("settings") or [])
55
+ return byid
56
+
57
+
58
+ def _label_of(node: dict, property_id: str) -> str | None:
59
+ s = _settings_index(node).get(property_id)
60
+ return s.get("label") if s else None
61
+
62
+
63
+ def placeholders(value) -> list[int]:
64
+ """The `<%N%>` indexes a parameter value binds, sorted — the value's variable contract."""
65
+ return sorted({int(m) for m in _PLACEHOLDER.findall(value)}) if isinstance(value, str) else []
66
+
67
+
68
+ def describe_node(flow: dict, node: dict) -> dict:
69
+ """One node's parameters with the designer label, value shape and bound variable names —
70
+ everything needed to pick the property to patch, without dumping the whole DTO."""
71
+ vmap = {v.get("id"): v.get("name") for v in flow.get("variables") or []}
72
+ params = []
73
+ for p in node.get("parameters") or []:
74
+ v = p.get("value")
75
+ bound = [{"index": ve.get("id"), "variable": vmap.get(ve.get("variableId")), "id": ve.get("variableId")}
76
+ for ve in (p.get("variable") or [])]
77
+ params.append({
78
+ "property": p.get("tabPropertyId"),
79
+ "label": _label_of(node, p.get("tabPropertyId")),
80
+ "kind": type(v).__name__ if v is not None else "null",
81
+ "editable": isinstance(v, str),
82
+ "value": v if isinstance(v, (str, int, float, type(None))) else "<structured>",
83
+ "binds": bound,
84
+ })
85
+ return {"node": node.get("actionName"), "id": node.get("id"),
86
+ "template": node.get("actionTemplateName"),
87
+ "disabled": bool(node.get("isDisabled")), "parameters": params}
88
+
89
+
90
+ def scan(flow: dict) -> list[dict]:
91
+ """Every node with its parameter surface."""
92
+ return [describe_node(flow, a) for a in flow.get("actions") or []]
93
+
94
+
95
+ def find_param(node: dict, key: str) -> dict | None:
96
+ """A parameter by tabPropertyId or by its designer label (case-insensitive)."""
97
+ params = node.get("parameters") or []
98
+ for p in params:
99
+ if p.get("tabPropertyId") == key:
100
+ return p
101
+ want = (key or "").strip().lower()
102
+ for p in params:
103
+ lbl = _label_of(node, p.get("tabPropertyId"))
104
+ if lbl and lbl.strip().lower() == want:
105
+ return p
106
+ return None
107
+
108
+
109
+ def set_param_value(node: dict, param: dict, new_value: str, *, allow_binding_change: bool = False) -> dict:
110
+ """Set one parameter's literal `value`, in place. Returns {changed, before, after}.
111
+
112
+ Refuses a non-string current value (structured DTO — builder territory) and, unless
113
+ `allow_binding_change`, refuses a new text whose `<%N%>` set differs from the old one: the
114
+ placeholder set IS the contract with `variable[]`, and silently dropping or inventing one
115
+ unbinds a variable at runtime with no validation error.
116
+ """
117
+ old = param.get("value")
118
+ if not isinstance(old, str):
119
+ raise ValueError(
120
+ f"parameter '{_label_of(node, param.get('tabPropertyId')) or param.get('tabPropertyId')}' "
121
+ f"holds a {type(old).__name__}, not text - structured parameters are edited through the "
122
+ f"process builder (process-edit), not as a text patch")
123
+ if not allow_binding_change and placeholders(old) != placeholders(new_value):
124
+ raise ValueError(
125
+ f"placeholder set would change {placeholders(old)} -> {placeholders(new_value)}; "
126
+ f"each <%N%> binds variable[] entry N, so this silently unbinds a variable. "
127
+ f"Pass --allow-binding-change only when variable[] is being rewritten too")
128
+ param["value"] = new_value
129
+ return {"changed": old != new_value, "before": old, "after": new_value}
130
+
131
+
132
+ def bind_param_var(node: dict, param: dict, bindings: dict, *, find=None, replace=None) -> dict:
133
+ """Set a parameter's `variable[]` bindings, optionally rewriting the value first, in place.
134
+
135
+ `bindings` maps each `<%N%>` index to a variable id. A NAMED token like `<%firstName%>` typed as
136
+ literal text is NOT a runtime placeholder (only positional `<%N%>` substitutes), so it renders
137
+ verbatim; the fix is to turn it INTO `<%0%>` and bind index 0 to the variable. Pass `find`/`replace`
138
+ to make that small substitution on the value here (so the 30 KB body need not be re-sent), then the
139
+ value's placeholder set must equal the binding index set, or the write is refused - a `<%N%>` with
140
+ no binding substitutes to nothing, a binding with no `<%N%>` is dead.
141
+ """
142
+ old = param.get("value")
143
+ if not isinstance(old, str):
144
+ raise ValueError(
145
+ f"parameter '{_label_of(node, param.get('tabPropertyId')) or param.get('tabPropertyId')}' "
146
+ f"holds a {type(old).__name__}, not text - structured parameters are builder territory")
147
+ value = old
148
+ if find is not None:
149
+ if find not in value:
150
+ raise ValueError(f"--find text is not present in the parameter value")
151
+ value = value.replace(find, replace if replace is not None else "")
152
+ idxs = set(placeholders(value))
153
+ if idxs != set(bindings):
154
+ raise ValueError(
155
+ f"after the edit the value binds placeholders {sorted(idxs)} but --bind covers "
156
+ f"{sorted(bindings)} - each <%N%> must have exactly one binding and vice versa")
157
+ param["value"] = value
158
+ param["variable"] = [{"id": i, "variableId": bindings[i], "attribute": None} for i in sorted(bindings)]
159
+ return {"changed": True, "value_changed": old != value, "before": old if old != value else None,
160
+ "after": value if old != value else None, "bindings": {i: bindings[i] for i in sorted(bindings)}}
161
+
162
+
163
+ # --- literal text replacement across a node's runtime + designer layers -------------------------
164
+ #
165
+ # Some values a flow author needs to change live INSIDE a structured parameter — a Map Data row's
166
+ # source expression, a decisional case's literal, a subprocess input's constant. Those are lists of
167
+ # dicts whose designer mirror is not a clean 1:1 with the runtime layer (the runtime holds `<%N%>`,
168
+ # the designer the variable GUID), so the normalizer deliberately leaves them alone and
169
+ # `set_param_value` refuses them. An EXACT-LITERAL replace is the one edit that is still safe there:
170
+ # the same literal appears verbatim in both layers, so replacing it in every string leaf of both
171
+ # keeps them consistent without either layer having to be understood.
172
+
173
+ def _walk_strings(obj, path: str, fn):
174
+ """Apply `fn(path, str) -> str` to every string leaf of a nested list/dict, in place."""
175
+ if isinstance(obj, dict):
176
+ for k, v in list(obj.items()):
177
+ p = f"{path}.{k}"
178
+ if isinstance(v, str):
179
+ obj[k] = fn(p, v)
180
+ else:
181
+ _walk_strings(v, p, fn)
182
+ elif isinstance(obj, list):
183
+ for i, v in enumerate(obj):
184
+ p = f"{path}[{i}]"
185
+ if isinstance(v, str):
186
+ obj[i] = fn(p, v)
187
+ else:
188
+ _walk_strings(v, p, fn)
189
+
190
+
191
+ def replace_text(node: dict, find: str, replace: str, *, property_key: str | None = None) -> list[dict]:
192
+ """Replace an exact literal in every string leaf of the node's runtime parameters AND designer
193
+ settings. Returns one hit per changed leaf: {layer, path, before, after, count}.
194
+
195
+ `property_key` narrows the edit to one parameter (by tabPropertyId or designer label); omit it to
196
+ sweep the whole node. Both layers are swept, which is what keeps runtime and designer consistent
197
+ for the structured settings the normalizer cannot regenerate.
198
+ """
199
+ if not find:
200
+ raise ValueError("--find must be a non-empty literal")
201
+ only = None
202
+ if property_key:
203
+ p = find_param(node, property_key)
204
+ if p is None:
205
+ raise ValueError(f"property not found on node '{node.get('actionName')}': {property_key}")
206
+ only = p.get("tabPropertyId")
207
+
208
+ hits: list[dict] = []
209
+
210
+ def sub(layer):
211
+ def fn(path, s):
212
+ if find not in s:
213
+ return s
214
+ new = s.replace(find, replace)
215
+ hits.append({"layer": layer, "path": path, "before": s, "after": new,
216
+ "count": s.count(find)})
217
+ return new
218
+ return fn
219
+
220
+ for p in node.get("parameters") or []:
221
+ if only and p.get("tabPropertyId") != only:
222
+ continue
223
+ v = p.get("value")
224
+ base = f"parameters[{p.get('tabPropertyId')}]"
225
+ if isinstance(v, str):
226
+ nv = sub("runtime")(base, v)
227
+ p["value"] = nv
228
+ else:
229
+ _walk_strings(v, base, sub("runtime"))
230
+
231
+ settings = _settings_index(node)
232
+ for sid, s in settings.items():
233
+ if only and sid != only:
234
+ continue
235
+ v = s.get("value")
236
+ base = f"customData[{sid}]"
237
+ if isinstance(v, str):
238
+ s["value"] = sub("designer")(base, v)
239
+ else:
240
+ _walk_strings(v, base, sub("designer"))
241
+ return hits
242
+
243
+
244
+ # --- process-variable type surgery --------------------------------------------------------------
245
+ #
246
+ # A flow variable is `{id, name, dataType, type, isList, isRequired, defaultValue}`. `type` is the
247
+ # DIRECTION: 10 input / 20 process / 30 output / 40 system / 50 auto. Inputs and outputs ARE the
248
+ # process's public contract (the run payload and the webhook response), so retyping one silently
249
+ # breaks every caller — that is why retyping them needs an explicit override here.
250
+ #
251
+ # System (40) and Auto (50) variables are written by the ENGINE, not by the process author: an Auto
252
+ # variable is one an action creates for its own outputs (Call Subprocess exposes the child instance
253
+ # ids that way) and its value is filled in at run time. Retyping or re-defaulting one either does
254
+ # nothing or breaks the action that owns it, so those edits are refused outright, with no override.
255
+
256
+ VAR_DIRECTION = {10: "input", 20: "process", 30: "output", 40: "system", 50: "auto"}
257
+ ENGINE_OWNED_DIRECTIONS = (40, 50)
258
+
259
+
260
+ def _refuse_engine_owned(var: dict, what: str) -> None:
261
+ direction = var.get("type")
262
+ if direction in ENGINE_OWNED_DIRECTIONS:
263
+ raise ValueError(
264
+ f"'{var.get('name')}' is a {VAR_DIRECTION[direction]} variable - the engine writes it, "
265
+ f"so its {what} is not the process author's to change")
266
+
267
+
268
+ def find_variable(flow: dict, key: str) -> dict | None:
269
+ """A variable by id or by name."""
270
+ variables = flow.get("variables") or []
271
+ for v in variables:
272
+ if v.get("id") == key:
273
+ return v
274
+ for v in variables:
275
+ if v.get("name") == key:
276
+ return v
277
+ return None
278
+
279
+
280
+ def set_variable_type(flow: dict, var: dict, data_type: str, *,
281
+ is_list: bool | None = None, allow_contract_change: bool = False) -> dict:
282
+ """Retype one flow variable in place. Returns {changed, before, after}.
283
+
284
+ Refuses an input (10) or output (30) variable without `allow_contract_change`: those are the run
285
+ payload and the response shape a caller depends on. Refuses a system (40) or auto (50) variable
286
+ always: the engine owns its type.
287
+ """
288
+ _refuse_engine_owned(var, "type")
289
+ direction = var.get("type")
290
+ if direction in (10, 30) and not allow_contract_change:
291
+ raise ValueError(
292
+ f"'{var.get('name')}' is an {VAR_DIRECTION.get(direction)} variable - retyping it changes "
293
+ f"the process's public contract (run payload / webhook response). Pass "
294
+ f"--allow-contract-change if that is genuinely intended")
295
+ before = {"dataType": var.get("dataType"), "isList": var.get("isList")}
296
+ var["dataType"] = data_type
297
+ if is_list is not None:
298
+ var["isList"] = is_list
299
+ after = {"dataType": var.get("dataType"), "isList": var.get("isList")}
300
+ return {"changed": before != after, "before": before, "after": after,
301
+ "direction": VAR_DIRECTION.get(direction, direction)}
302
+
303
+
304
+
305
+ def set_variable_required(flow: dict, var: dict, required: bool, *,
306
+ allow_contract_change: bool = False) -> dict:
307
+ """Set one INPUT variable's `isRequired` flag in place. Returns {changed, before, after}.
308
+
309
+ Only an input (10) variable has a caller-supplied value, so `isRequired` is meaningless
310
+ anywhere else and setting it there is refused rather than silently written.
311
+
312
+ The two directions are NOT symmetric, and only one of them is guarded:
313
+
314
+ * **Clearing** it (required -> optional) can never break an existing caller: every payload
315
+ that was valid before is still valid. It is allowed outright.
316
+ * **Setting** it (optional -> required) tightens the public contract and breaks every caller
317
+ that legitimately omitted the field, so it needs `allow_contract_change`.
318
+
319
+ Clearing the flag is safe for CALLERS but not automatically safe for the FLOW: a Node body
320
+ that injects the variable through a bare raw placeholder (`var f = <%6%>;`) becomes
321
+ `var f = ;` -- a SyntaxError -- the first time the value is genuinely absent. Guard such an
322
+ injection as `[<%6%>][0]` (which yields `undefined`) BEFORE clearing the flag. This function
323
+ cannot see into node bodies, so it cannot check that for you.
324
+ """
325
+ direction = var.get("type")
326
+ if direction != 10:
327
+ raise ValueError(
328
+ f"'{var.get('name')}' is a {VAR_DIRECTION.get(direction, direction)} variable - only an "
329
+ f"input variable carries a caller-supplied value, so isRequired means nothing on it")
330
+ required = bool(required)
331
+ before = {"isRequired": var.get("isRequired")}
332
+ if required and not before["isRequired"] and not allow_contract_change:
333
+ raise ValueError(
334
+ f"making input '{var.get('name')}' required tightens the process's public contract and "
335
+ f"breaks every caller that omits it. Pass --allow-contract-change if that is intended")
336
+ var["isRequired"] = required
337
+ after = {"isRequired": var.get("isRequired")}
338
+ return {"changed": before != after, "before": before, "after": after,
339
+ "direction": VAR_DIRECTION.get(direction, direction)}
340
+
341
+
342
+
343
+ VAR_DIRECTION_BY_NAME = {"input": 10, "process": 20, "output": 30}
344
+
345
+ _DATATYPE_ALIASES = {
346
+ "boolean": "0317bfee-b2f5-4bde-bfe8-121212121210",
347
+ "integer": "0317bfee-b2f5-4bde-bfe8-121212121211",
348
+ "float": "0317bfee-b2f5-4bde-bfe8-121212121212",
349
+ "double": "0317bfee-b2f5-4bde-bfe8-121212121213",
350
+ "string": "0317bfee-b2f5-4bde-bfe8-121212121214",
351
+ "date": "0317bfee-b2f5-4bde-bfe8-121212121215",
352
+ "time": "0317bfee-b2f5-4bde-bfe8-121212121217",
353
+ "datetime":"0317bfee-b2f5-4bde-bfe8-121212121218",
354
+ "guid": "0317bfee-b2f5-4bde-bfe8-121212121222",
355
+ "json": "0317bfee-b2f5-4bde-bfe8-121212121220",
356
+ "object": "0317bfee-b2f5-4bde-bfe8-121212121221",
357
+ "file": "10c6ac59-3929-49e6-99dc-121212121219",
358
+ }
359
+
360
+
361
+ def resolve_data_type(spec: str) -> str:
362
+ """A friendly alias ('string', 'file') or a data-type GUID -> the GUID."""
363
+ key = str(spec or "").strip().lower()
364
+ if key in _DATATYPE_ALIASES:
365
+ return _DATATYPE_ALIASES[key]
366
+ if "-" in str(spec):
367
+ return str(spec).strip()
368
+ raise ValueError(
369
+ f"unknown data type {spec!r}; use a GUID or one of: {', '.join(sorted(_DATATYPE_ALIASES))}")
370
+
371
+
372
+ def add_variable(flow: dict, name: str, data_type: str, direction: str, *,
373
+ is_list: bool = False, default_value=None, is_required: bool = False,
374
+ new_id: str | None = None) -> dict:
375
+ """Append one variable to a flow in place. Returns the variable that was added.
376
+
377
+ A flow's variables are addressed BY ID everywhere else - node parameter binds, a form's
378
+ input/output maps - so adding one is safe for existing wiring in a way that renaming or
379
+ retyping is not: nothing can already point at an id that did not exist.
380
+
381
+ Refused rather than guessed:
382
+ * a duplicate NAME, even though the platform keys on id. Two variables sharing a name make
383
+ every later name-based lookup ambiguous, and the form-event tooling resolves names to ids.
384
+ * an unknown direction. 10/20/30 are input/process/output; a variable with no direction is
385
+ not addressable from either the run payload or the response.
386
+ """
387
+ import uuid as _uuid
388
+
389
+ nm = str(name or "").strip()
390
+ if not nm:
391
+ raise ValueError("a variable needs a name")
392
+ dirn = str(direction or "").strip().lower()
393
+ if dirn not in VAR_DIRECTION_BY_NAME:
394
+ raise ValueError(f"direction must be one of input, process, output - got {direction!r}")
395
+ for v in flow.get("variables") or []:
396
+ if str(v.get("name")) == nm:
397
+ raise ValueError(f"a variable named {nm!r} already exists in this flow "
398
+ f"(id {v.get('id')}); pick another name")
399
+ var = {
400
+ "id": new_id or str(_uuid.uuid4()),
401
+ "contextId": None,
402
+ "dataType": resolve_data_type(data_type),
403
+ "type": VAR_DIRECTION_BY_NAME[dirn],
404
+ "name": nm,
405
+ "defaultValue": default_value,
406
+ "isList": bool(is_list),
407
+ "isError": False,
408
+ "isRequired": bool(is_required),
409
+ }
410
+ flow.setdefault("variables", []).append(var)
411
+ return var
412
+
413
+
414
+
415
+ def set_variable_default(flow: dict, var: dict, default_value, *,
416
+ allow_contract_change: bool = False) -> dict:
417
+ """Set one variable's `defaultValue` in place. Returns {changed, before, after}.
418
+
419
+ This is how an input stops being something the caller must supply. Verified on a File
420
+ variable: a run that omits it entirely still receives the object, and the platform
421
+ RE-STAGES the referenced file into the calling flow's own instance path. So a value a user
422
+ should never have to provide - a configuration, a reference table, an empty placeholder -
423
+ can be set once by an admin and then simply is not a field on anyone's form.
424
+
425
+ Two cautions, both learned rather than assumed:
426
+
427
+ * A File default carries a `path` pointing into some flow instance's storage. If that
428
+ instance is cleaned up - data retention is per-flow and finite - the default becomes a
429
+ dangling reference. Stamp defaults from a file whose lifetime you control, and re-check
430
+ after any retention change.
431
+ * Changing an input's default changes what a run does when the caller says nothing. That
432
+ is a contract change in every sense that matters, even though the signature is unchanged,
433
+ so an input/output variable needs `allow_contract_change`.
434
+
435
+ A system (40) or auto (50) variable is refused always: the engine fills its value at run time.
436
+ """
437
+ _refuse_engine_owned(var, "default value")
438
+ direction = var.get("type")
439
+ if direction in (10, 30) and not allow_contract_change:
440
+ raise ValueError(
441
+ f"'{var.get('name')}' is an {VAR_DIRECTION.get(direction)} variable - changing its "
442
+ f"default changes what a run does when the caller supplies nothing. Pass "
443
+ f"--allow-contract-change if that is intended")
444
+ before = {"defaultValue": var.get("defaultValue")}
445
+ var["defaultValue"] = default_value
446
+ after = {"defaultValue": var.get("defaultValue")}
447
+ return {"changed": before != after, "before": before, "after": after,
448
+ "direction": VAR_DIRECTION.get(direction, direction)}
449
+
450
+
451
+
452
+ def _camel(key: str) -> str:
453
+ """`TabPropertyId` -> `tabPropertyId`. The DTO builder emits PascalCase (the CREATE shape);
454
+ a flow read back from the API is camelCase. Splicing one into the other without this is the
455
+ silent-corruption case: the API accepts both, the designer renders neither consistently."""
456
+ return key[:1].lower() + key[1:] if key else key
457
+
458
+
459
+ def to_live_action(dto: dict) -> dict:
460
+ """Convert a builder ActionDto (PascalCase) into the camelCase shape a LIVE flow carries.
461
+
462
+ Only the top level and the `parameters` rows need it - `customData` is already authored in
463
+ the live casing by the builder, and its `configuration` tree is copied from the template.
464
+ """
465
+ out = {}
466
+ for k, v in dto.items():
467
+ ck = _camel(k)
468
+ if ck == "parameters" and isinstance(v, list):
469
+ out[ck] = [{_camel(pk): pv for pk, pv in row.items()} if isinstance(row, dict) else row
470
+ for row in v]
471
+ elif ck == "ports" and isinstance(v, list):
472
+ out[ck] = [{_camel(pk): pv for pk, pv in row.items()} if isinstance(row, dict) else row
473
+ for row in v]
474
+ else:
475
+ out[ck] = v
476
+ return out
477
+
478
+
479
+ def insert_node(flow: dict, after: dict, action: dict) -> tuple[bool, str]:
480
+ """Splice one already-built action into a flow immediately AFTER `after`, in place.
481
+
482
+ Ports live on the SOURCE action and carry `destinationId`, so an insertion is two edits and
483
+ not one: the new node gets a port to whatever `after` pointed at, and `after`'s own outgoing
484
+ port is repointed at the new node. Do only the first and the new node is unreachable; do only
485
+ the second and the tail of the flow is orphaned. Both failures validate.
486
+
487
+ Refused rather than guessed: an anchor with more than one outgoing port (which branch the new
488
+ node belongs on is a design decision), and an anchor that is not in this flow.
489
+ """
490
+ actions = flow.get("actions") or []
491
+ anchor_id = after.get("id")
492
+ if not any(a.get("id") == anchor_id for a in actions):
493
+ return False, "anchor %r is not in this flow" % (after.get("actionName") or anchor_id)
494
+
495
+ outgoing = [p for p in after.get("ports") or []
496
+ if p.get("sourceId") == anchor_id and p.get("destinationId")]
497
+ if len(outgoing) > 1:
498
+ return False, ("anchor %r has %d outgoing ports - which branch the new node belongs on is a "
499
+ "design decision, so wire it explicitly instead"
500
+ % (after.get("actionName"), len(outgoing)))
501
+
502
+ new_id = action.get("id")
503
+ successor = outgoing[0]["destinationId"] if outgoing else None
504
+ action.setdefault("ports", [])
505
+ if successor:
506
+ action["ports"].append({
507
+ "id": str(__import__("uuid").uuid4()), "flowId": flow.get("id"),
508
+ "sourceId": new_id, "destinationId": successor,
509
+ "type": 0, "state": 1, "data": {}, "errors": {}, "config": {}})
510
+ outgoing[0]["destinationId"] = new_id
511
+ tail = "between %r and its successor" % (after.get("actionName"),)
512
+ else:
513
+ tail = "after %r, which had no successor" % (after.get("actionName"),)
514
+
515
+ actions.append(action)
516
+ flow["actions"] = actions
517
+ return True, "inserted %r (%s) %s" % (action.get("actionName"),
518
+ action.get("actionTemplateName"), tail)
519
+
520
+
521
+ def set_process_title(flow: dict, title: str) -> dict:
522
+ """Set a flow's title in place. Returns {changed, before, after}.
523
+
524
+ The title is cosmetic - the platform wires everything by id, never by name (same reasoning as
525
+ rename-actions) - so this touches only the top-level `title` field, no node parameter and no
526
+ designer customData, and cannot break a flow. A `duplicate` always lands as '... (Copy)', so this
527
+ is the follow-up that gives the copy a real name.
528
+ """
529
+ before = flow.get("title")
530
+ flow["title"] = title
531
+ return {"changed": before != title, "before": before, "after": title}
532
+
533
+
534
+ # --------------------------------------------------------------------------- node removal
535
+
536
+ _TERMINALS = ("Start", "Stop")
537
+
538
+
539
+ def delete_node(flow: dict, node: dict) -> tuple[bool, str]:
540
+ """Remove one action from a flow and reconnect the graph around it, in place.
541
+
542
+ A dead node is not free: it still executes, still bills an execution, and still throws into
543
+ whatever error variable it was given, so a node whose upstream contract moved on fails on every
544
+ single run with nothing downstream to show for it. Deleting it over the API means healing the
545
+ edges by hand — every port that pointed AT this node is re-pointed at the node's own successor,
546
+ or dropped when it has none. Miss that and the tail of the flow is stranded.
547
+
548
+ Refused rather than guessed: `Start` / `Stop` (a flow needs both), a node with more than one
549
+ outgoing port (which successor inherits the incoming edges is a design decision, not a default),
550
+ and a node that is not in this flow. Variables are left alone — another node may still read them,
551
+ and an unused variable costs nothing. Returns (changed, message).
552
+ """
553
+ actions = flow.get("actions") or []
554
+ if not any(a is node or a.get("id") == node.get("id") for a in actions):
555
+ return False, "node %r is not in this flow" % (node.get("actionName") or node.get("id"))
556
+
557
+ template = node.get("actionTemplateName")
558
+ if template in _TERMINALS:
559
+ return False, "refusing to delete the %s node - a flow needs both Start and Stop" % template
560
+
561
+ outgoing = [p for p in node.get("ports") or []
562
+ if p.get("destinationId") and p.get("sourceId") == node.get("id")]
563
+ if len(outgoing) > 1:
564
+ return False, ("node %r has more than one outgoing port - which successor inherits its "
565
+ "incoming edges is a design decision, so rewire it explicitly first"
566
+ % node.get("actionName"))
567
+
568
+ node_id = node.get("id")
569
+ successor = outgoing[0]["destinationId"] if outgoing else None
570
+
571
+ rewired, dropped = 0, 0
572
+ for action in actions:
573
+ if action.get("id") == node_id:
574
+ continue
575
+ kept = []
576
+ reachable = {p.get("destinationId") for p in action.get("ports") or []
577
+ if p.get("destinationId") != node_id}
578
+ for port in action.get("ports") or []:
579
+ if port.get("destinationId") != node_id:
580
+ kept.append(port)
581
+ continue
582
+ # A self-loop or a duplicate edge is worse than a missing one: the designer renders both
583
+ # and the engine follows them, so heal only into a successor this node does not reach.
584
+ if successor and successor != action.get("id") and successor not in reachable:
585
+ port["destinationId"] = successor
586
+ port["flowId"] = flow.get("id", port.get("flowId"))
587
+ reachable.add(successor)
588
+ kept.append(port)
589
+ rewired += 1
590
+ else:
591
+ dropped += 1
592
+ action["ports"] = kept
593
+
594
+ flow["actions"] = [a for a in actions if a.get("id") != node_id]
595
+ return True, ("deleted %r (%s); %d edge(s) rewired to its successor, %d dropped"
596
+ % (node.get("actionName"), template, rewired, dropped))
597
+
598
+
599
+ # --------------------------------------------------------------------------- error ports
600
+
601
+ # ErrorDataModel: the only datatype the engine accepts on an error-port variable. A plain string
602
+ # variable there validates and the port never fires, which is the silent version of this failing.
603
+ ERROR_DATA_MODEL = "10c6ac59-3929-49e6-99dc-121212121220"
604
+ # Per PROCESIO these templates expose no error port at all.
605
+ NO_ERROR_PORT = ("start", "stop", "join", "for each", "foreach")
606
+
607
+
608
+ def set_error_port(flow: dict, node: dict, handler: dict | None, *, clear: bool = False) -> dict:
609
+ """Route one node's error port to `handler`, in place. Returns a report.
610
+
611
+ An error port is THREE things and wiring any two of them is worse than wiring none, because
612
+ the flow still validates: the port itself (`type: 1`, `data: {"isDefault": "error"}`), the
613
+ node's `variableErrorId`, and that variable being `isError` of the error datatype. Written
614
+ together here so a caller cannot produce the half-wired shape.
615
+
616
+ What this buys, and why it is not just error cosmetics: a node that faults takes the whole
617
+ flow down at status 40, so a flow that a FORM launches on load cannot populate the form at
618
+ all. Sending the fault to the node's own successor lets the flow reach Stop and the form
619
+ render with that one section empty - provided the successor tolerates a null input, which is
620
+ the caller's judgement and is why the handler is named rather than inferred.
621
+
622
+ Refused rather than guessed: a template with no error port, a handler outside this flow, and
623
+ a handler that is the node itself (a self-loop the designer draws and the engine follows).
624
+ """
625
+ import uuid as _uuid
626
+
627
+ template = str(node.get("actionTemplateName") or "").strip()
628
+ if template.lower() in NO_ERROR_PORT:
629
+ raise ValueError("%s has no error port, so there is nothing to route" % (template or "this action"))
630
+
631
+ node_id = node.get("id")
632
+ actions = flow.get("actions") or []
633
+ if not any(a.get("id") == node_id for a in actions):
634
+ raise ValueError("node %r is not in this flow" % (node.get("actionName") or node_id))
635
+
636
+ ports = [p for p in (node.get("ports") or [])]
637
+ existing = [p for p in ports if p.get("type") == 1]
638
+ before = existing[0].get("destinationId") if existing else None
639
+
640
+ if clear:
641
+ node["ports"] = [p for p in ports if p.get("type") != 1]
642
+ freed = node.get("variableErrorId")
643
+ node["variableErrorId"] = None
644
+ # Drop the variable too, but only when nothing else still points at it.
645
+ if freed and not any(a.get("variableErrorId") == freed for a in actions):
646
+ flow["variables"] = [v for v in (flow.get("variables") or []) if v.get("id") != freed]
647
+ return {"node": node.get("actionName"), "cleared": True, "handler_before": before,
648
+ "handler": None, "variable_error_id": None}
649
+
650
+ if handler is None:
651
+ raise ValueError("a handler is required unless clearing")
652
+ handler_id = handler.get("id")
653
+ if handler_id == node_id:
654
+ raise ValueError("a node cannot handle its own error - name a different action")
655
+ if not any(a.get("id") == handler_id for a in actions):
656
+ raise ValueError("handler %r is not in this flow" % (handler.get("actionName") or handler_id))
657
+
658
+ var_id = node.get("variableErrorId")
659
+ var = next((v for v in (flow.get("variables") or []) if v.get("id") == var_id), None)
660
+ if var is None:
661
+ base = "%s_error" % (node.get("actionName") or "node")
662
+ taken = {str(v.get("name")) for v in (flow.get("variables") or [])}
663
+ name, n = base, 2
664
+ while name in taken:
665
+ name, n = "%s_%d" % (base, n), n + 1
666
+ var = {"id": str(_uuid.uuid4()), "contextId": None, "dataType": ERROR_DATA_MODEL,
667
+ "type": 20, "name": name, "defaultValue": None, "isList": False,
668
+ "isError": True, "isRequired": False}
669
+ flow.setdefault("variables", []).append(var)
670
+ created_var = True
671
+ else:
672
+ # An existing binding of the wrong shape is the half-wired case: repair it rather than
673
+ # trusting it, since the flow validates either way.
674
+ var["dataType"], var["isError"] = ERROR_DATA_MODEL, True
675
+ created_var = False
676
+ node["variableErrorId"] = var["id"]
677
+
678
+ node["ports"] = [p for p in ports if p.get("type") != 1] + [{
679
+ "id": str(_uuid.uuid4()), "flowId": flow.get("id"), "sourceId": node_id,
680
+ "destinationId": handler_id, "type": 1, "state": 1,
681
+ "data": {"isDefault": "error"}, "errors": {}, "config": {}}]
682
+
683
+ return {"node": node.get("actionName"), "cleared": False, "handler_before": before,
684
+ "handler": handler.get("actionName"), "variable_error_id": var["id"],
685
+ "variable": var["name"], "variable_created": created_var}
686
+
687
+
688
+ # --------------------------------------------------------------------------- Query Store nodes
689
+
690
+ # The Query Store node's property ids end in these suffixes: the statement itself (a code-editor)
691
+ # and the map that binds its @params. Matched on the suffix because the prefix is the template's.
692
+ QUERY_PROP_SUFFIX = "b102"
693
+ PARAMS_PROP_SUFFIX = "b103"
694
+
695
+
696
+ def find_query_params(node: dict) -> tuple[dict | None, dict | None]:
697
+ """Return (statement parameter, parameters-map parameter) of a Query Store node.
698
+
699
+ Both are needed together by any caller that renumbers the statement's chips: a node numbers
700
+ its query chips and its Parameters-map sources from ONE `<%N%>` sequence, so a caller that
701
+ rewrites the statement without looking at the map can move a bound @param's source without
702
+ touching it.
703
+ """
704
+ query = pmap = None
705
+ for p in node.get("parameters") or []:
706
+ tab = str(p.get("tabPropertyId") or "")
707
+ if tab.endswith(QUERY_PROP_SUFFIX):
708
+ query = p
709
+ elif tab.endswith(PARAMS_PROP_SUFFIX):
710
+ pmap = p
711
+ return query, pmap
712
+
713
+
714
+ def renumber_node_placeholders(node: dict, order: list[dict]) -> int:
715
+ """Renumber a node's `<%N%>` placeholders so one sequence runs across ALL its parameters.
716
+
717
+ A node does not number each parameter independently: the statement's chips, the
718
+ parameters-map sources and the output bindings draw from ONE sequence. So growing the map by
719
+ two rows silently moves the output bindings, and an output that still says `<%8%>` while its
720
+ binding moved to 10 is written back to the wrong variable - a flow that validates and quietly
721
+ loses its result.
722
+
723
+ `order` is the parameters in the sequence they should occupy; any parameter of the node not
724
+ listed keeps its place at the end, in its existing index order. Returns the next free index.
725
+ """
726
+ listed = [p for p in order if p is not None]
727
+ listed_ids = {id(p) for p in listed}
728
+ rest = [p for p in (node.get("parameters") or []) if id(p) not in listed_ids]
729
+
730
+ def first_index(p):
731
+ hits = placeholders(p.get("value")) if not isinstance(p.get("value"), str) else \
732
+ [int(x) for x in _PLACEHOLDER.findall(p.get("value") or "")]
733
+ return min(hits) if hits else 10 ** 6
734
+
735
+ rest.sort(key=first_index)
736
+
737
+ nxt = 0
738
+ for p in listed + rest:
739
+ mapping: dict[int, int] = {}
740
+ for old in sorted(_collect_indices(p)):
741
+ mapping[old] = nxt
742
+ nxt += 1
743
+ if mapping:
744
+ _apply_mapping(p, mapping)
745
+ return nxt
746
+
747
+
748
+ def _collect_indices(param: dict) -> set:
749
+ """Every placeholder index this parameter carries.
750
+
751
+ Two sources, and only two: a `<%N%>` token in any string, and the `id` of an entry inside a
752
+ `variable` list. An `id` anywhere else is NOT an index - a parameters-map row carries its own
753
+ ordinal `id`, which collides with the low indices and would be renumbered into nonsense.
754
+ """
755
+ found: set = set()
756
+
757
+ def walk(o, in_variable=False):
758
+ if isinstance(o, dict):
759
+ if in_variable and isinstance(o.get("id"), int):
760
+ found.add(o["id"])
761
+ for k, v in o.items():
762
+ walk(v, in_variable=(k == "variable"))
763
+ elif isinstance(o, list):
764
+ for x in o:
765
+ walk(x, in_variable=in_variable)
766
+ elif isinstance(o, str):
767
+ for m in _PLACEHOLDER.findall(o):
768
+ found.add(int(m))
769
+
770
+ walk(param.get("value"))
771
+ walk(param.get("variable") or [], in_variable=True)
772
+ return found
773
+
774
+
775
+ def _apply_mapping(param: dict, mapping: dict) -> None:
776
+ def walk(o, in_variable=False):
777
+ if isinstance(o, dict):
778
+ out = {}
779
+ for k, v in o.items():
780
+ if k == "id" and in_variable and isinstance(v, int):
781
+ out[k] = mapping.get(v, v)
782
+ else:
783
+ out[k] = walk(v, in_variable=(k == "variable"))
784
+ return out
785
+ if isinstance(o, list):
786
+ return [walk(x, in_variable=in_variable) for x in o]
787
+ if isinstance(o, str):
788
+ return _PLACEHOLDER.sub(
789
+ lambda m: "<%%%d%%>" % mapping.get(int(m.group(1)), int(m.group(1))), o)
790
+ return o
791
+
792
+ param["value"] = walk(param.get("value"))
793
+ for b in param.get("variable") or []:
794
+ if isinstance(b.get("id"), int):
795
+ b["id"] = mapping.get(b["id"], b["id"])
796
+
797
+
798
+ def value_placeholders(value) -> set:
799
+ """Every `<%N%>` index in a parameter's VALUE, however deeply nested.
800
+
801
+ Separate from _collect_indices, which also counts binding ids: this answers "what does the
802
+ text ask for", which is the half that has to match the bindings.
803
+ """
804
+ found: set = set()
805
+
806
+ def walk(o):
807
+ if isinstance(o, dict):
808
+ for v in o.values():
809
+ walk(v)
810
+ elif isinstance(o, list):
811
+ for x in o:
812
+ walk(x)
813
+ elif isinstance(o, str):
814
+ for m in _PLACEHOLDER.findall(o):
815
+ found.add(int(m))
816
+
817
+ walk(value)
818
+ return found
819
+
820
+
821
+ def add_param_bindings(node: dict, param: dict, bindings: dict, *,
822
+ find=None, replace=None) -> dict:
823
+ """Add or replace bindings on a parameter, optionally editing its text in the same write.
824
+
825
+ Why this exists next to bind_param_var: that one takes a STRING parameter and REPLACES the
826
+ whole binding list, which is right for a small text parameter and useless for the two edits
827
+ that actually come up - reaching inside a STRUCTURED parameter (an HTTP body template is a
828
+ dict), and ADDING one binding to a parameter that already has nine without restating them.
829
+
830
+ `bindings` maps an index to {variableId, attributeId}. An attribute path matters: a binding
831
+ into a loop item's field is variable + attribute, and a binding with the attribute dropped
832
+ silently resolves to the whole object.
833
+
834
+ Refused rather than guessed: an edit that leaves a `<%N%>` with no binding (it substitutes to
835
+ nothing, and an empty value in a JSON body is usually accepted and then wrong) or a binding
836
+ with no `<%N%>` (dead weight that the next reader has to explain).
837
+ """
838
+ value = param.get("value")
839
+ if find is not None:
840
+ hits = [0]
841
+
842
+ def sub(_path, s):
843
+ if find in s:
844
+ hits[0] += s.count(find)
845
+ return s.replace(find, replace if replace is not None else "")
846
+ return s
847
+
848
+ if isinstance(value, str):
849
+ if find in value:
850
+ hits[0] += value.count(find)
851
+ value = value.replace(find, replace if replace is not None else "")
852
+ param["value"] = value
853
+ else:
854
+ _walk_strings(value, "", sub)
855
+ if not hits[0]:
856
+ raise ValueError("--find text is not present in this parameter")
857
+
858
+ kept = {b["id"]: b for b in (param.get("variable") or []) if isinstance(b.get("id"), int)}
859
+ for idx, spec in bindings.items():
860
+ kept[idx] = {"id": idx, "variableId": spec["variableId"],
861
+ "attribute": ({"attributeId": spec["attributeId"], "nextAttribute": None}
862
+ if spec.get("attributeId") else None)}
863
+ asked = value_placeholders(param.get("value"))
864
+ if asked != set(kept):
865
+ raise ValueError(
866
+ "after the edit the value uses placeholders %s but the bindings cover %s - each "
867
+ "<%%N%%> needs exactly one binding and vice versa"
868
+ % (sorted(asked), sorted(kept)))
869
+ param["variable"] = [kept[i] for i in sorted(kept)]
870
+ return {"changed": True, "replacements": hits[0] if find is not None else 0,
871
+ "bindings_added": sorted(bindings), "bindings_total": len(kept)}