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,736 @@
1
+ """Bridge the layout engine to the `.procesio` export / live flow DTO.
2
+
3
+ Reads a flow with the flow graph reader, runs the pure engine, and writes the new
4
+ positions back into each action's `CustomData.position` (and a For-Each area's
5
+ `areaSize`), resetting `CanvasData`. Case-insensitive for read AND write (PascalCase
6
+ exports / camelCase live). Returns a NEW object — never mutates the input.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import copy
11
+
12
+ from tools.procesio.flowmodel import read_bundle, read_flow
13
+ from tools.procesio.layout import cycles, dispatch, engine, report
14
+
15
+
16
+ def _key(d: dict, *names: str):
17
+ """The actual key present in `d` matching any of `names` (case-insensitive)."""
18
+ low = {str(k).lower(): k for k in d}
19
+ for n in names:
20
+ if n.lower() in low:
21
+ return low[n.lower()]
22
+ return None
23
+
24
+
25
+ def _get(d, *names, default=None):
26
+ k = _key(d, *names) if isinstance(d, dict) else None
27
+ return d[k] if k else default
28
+
29
+
30
+ def _flows(obj: dict):
31
+ k = _key(obj, "Flows")
32
+ return (obj[k], k) if k else (None, None)
33
+
34
+
35
+ def _round(p: dict) -> dict:
36
+ return {"x": round(float(p["x"]), 2), "y": round(float(p["y"]), 2)}
37
+
38
+
39
+ def _layout_score(bundle: dict, flow_id: str | None) -> float:
40
+ """Lower = more readable. Hard issues dominate, then crossings, then long edges + overall
41
+ size. Uses the engine-agnostic readability report, so both engines are judged identically."""
42
+ rep = report.build_report(bundle, flow_id)
43
+ m = rep["metrics"]
44
+ return (rep["hard_issue_count"] * 10000.0 + m["crossings"] * 100.0
45
+ + m["max_edge_px"] / 50.0 + (m["width"] + m["height"]) / 500.0)
46
+
47
+
48
+ def _best_of_engines(obj: dict, flow_id: str | None = None,
49
+ only: list[str] | None = None) -> dict:
50
+ """LAYOUT_ENGINE=auto: lay the flow out with BOTH engines and return whichever the
51
+ readability report scores better. Legacy runs with its opt-in cluster + min-crossings
52
+ layers; ELK uses its own built-in clustering. Neither engine is modified — this only
53
+ picks between their OUTPUTS, so 'what works' (e.g. the legacy retry loop-curl) is never
54
+ lost: it just wins the score where it's better. Falls back to legacy if ELK errors or
55
+ for `only`/subset mode (ELK has no partial layout)."""
56
+ import os
57
+ saved = os.environ.get("LAYOUT_ENGINE")
58
+ try:
59
+ os.environ["LAYOUT_ENGINE"] = dispatch.LEGACY
60
+ legacy = layout_flow(obj, flow_id=flow_id, only=only,
61
+ cluster=True, minimize_crossings=True)
62
+ elk = None
63
+ if only is None:
64
+ os.environ["LAYOUT_ENGINE"] = dispatch.ELK
65
+ try:
66
+ elk = layout_flow(obj, flow_id=flow_id)
67
+ except Exception: # noqa: BLE001
68
+ elk = None
69
+ finally:
70
+ if saved is None:
71
+ os.environ.pop("LAYOUT_ENGINE", None)
72
+ else:
73
+ os.environ["LAYOUT_ENGINE"] = saved
74
+ if elk is None:
75
+ return legacy
76
+ if _layout_score(elk["bundle"], elk["flow_id"]) < _layout_score(legacy["bundle"], legacy["flow_id"]):
77
+ return elk
78
+ return legacy
79
+
80
+
81
+ def _apply_fan_lanes(positions: dict, fg) -> dict:
82
+ """The reference FAN pattern (hand-arranged entry spine): when a decisional's parallel alternative
83
+ branches all reconnect at the SAME merge, stack each branch as a flat LANE parallel to
84
+ the spine axis -- lanes at +/-GAP around the split->merge row, bodies spread EVENLY
85
+ between split.x and merge.x (a 1-node branch sits at the midpoint), keeping the side
86
+ the engine chose for each branch. Dead-end stubs are left alone here; the branch
87
+ conventions pass re-places them outward off the new lane bodies afterwards.
88
+
89
+ The base layered engine has no concept of sibling branches between one split and one
90
+ merge, so it gives each branch a semi-arbitrary diagonal. This pass is applied by
91
+ layout_flow ONLY when the final layout is strictly better (overlaps, crossings,
92
+ maxedge) than without it -- ties keep the existing output, so approved layouts can't
93
+ churn."""
94
+ from collections import defaultdict
95
+ P = positions
96
+ kind = {n.id: n.shape for n in fg.nodes}
97
+ parent = {n.id: n.parent_id for n in fg.nodes}
98
+ nsucc: dict = defaultdict(list)
99
+ pred: dict = defaultdict(list)
100
+ for e in fg.edges:
101
+ if e.source in P and e.dest in P:
102
+ if getattr(e, "type", 0) == 0:
103
+ nsucc[e.source].append(e.dest)
104
+ pred[e.dest].append(e.source)
105
+ GAP = 180.0
106
+
107
+ def deadend_private(start, src):
108
+ """True if `start` heads a private chain that terminates (a stub -- not a branch)."""
109
+ if len(pred.get(start, [])) != 1:
110
+ return False
111
+ seen, cur = {start, src}, start
112
+ while True:
113
+ nx = nsucc.get(cur, [])
114
+ if not nx:
115
+ return True
116
+ if len(nx) != 1:
117
+ return False
118
+ nxt = nx[0]
119
+ if nxt in seen or len(pred.get(nxt, [])) != 1:
120
+ return False
121
+ seen.add(nxt)
122
+ cur = nxt
123
+
124
+ for s in sorted(P):
125
+ if kind.get(s) != "diamond" or parent.get(s):
126
+ continue
127
+ branches: list = []
128
+ merge = None
129
+ ok = True
130
+ for t in dict.fromkeys(nsucc.get(s, [])):
131
+ if len(pred.get(t, [])) > 1: # direct edge to the merge (e.g. default->Join)
132
+ if merge is None:
133
+ merge = t
134
+ elif merge != t:
135
+ ok = False
136
+ continue
137
+ if deadend_private(t, s): # stub off the split -- not a fan branch
138
+ continue
139
+ body, cur, guard = [], t, 0
140
+ while ok and guard < 64:
141
+ guard += 1
142
+ if parent.get(cur) or kind.get(cur) == "area":
143
+ ok = False
144
+ break
145
+ body.append(cur)
146
+ cont = [x for x in nsucc.get(cur, []) if not deadend_private(x, cur)]
147
+ if len(cont) != 1:
148
+ ok = False
149
+ break
150
+ nxt = cont[0]
151
+ if len(pred.get(nxt, [])) > 1: # merge reached
152
+ if merge is None:
153
+ merge = nxt
154
+ elif merge != nxt:
155
+ ok = False
156
+ break
157
+ cur = nxt
158
+ else:
159
+ ok = False
160
+ if not ok:
161
+ break
162
+ branches.append(body)
163
+ if not ok or len(branches) < 2 or merge is None or merge not in P or parent.get(merge):
164
+ continue
165
+ sx, mx = P[s]["x"], P[merge]["x"]
166
+ if mx - sx < 2 * GAP: # merge must sit meaningfully to the right
167
+ continue
168
+ spine_y = (P[s]["y"] + P[merge]["y"]) / 2.0
169
+ # balanced lanes STRADDLING the spine (the reference fan: ceil(n/2) above, rest below),
170
+ # preserving the engine's vertical order of the branches -- his 0GetInfo golden:
171
+ # 3 branches -> lanes -2, -1, +1.
172
+ infos = sorted([(sorted(P[b]["y"] for b in body)[len(body) // 2], body)
173
+ for body in branches], key=lambda bi: bi[0])
174
+ n_above = (len(infos) + 1) // 2
175
+ lanes = [(-(n_above - i), body) for i, (_m, body) in enumerate(infos[:n_above])]
176
+ lanes += [(+(i + 1), body) for i, (_m, body) in enumerate(infos[n_above:])]
177
+ def _stub_chain(start, src):
178
+ out, cur, seen = [start], start, {start, src}
179
+ while True:
180
+ nx = nsucc.get(cur, [])
181
+ if len(nx) != 1 or nx[0] in seen:
182
+ return out
183
+ out.append(nx[0])
184
+ seen.add(nx[0])
185
+ cur = nx[0]
186
+
187
+ for lane, body in lanes:
188
+ y = spine_y + lane * GAP
189
+ step = (mx - sx) / (len(body) + 1)
190
+ for i, b in enumerate(body):
191
+ nx_, ny_ = sx + (i + 1) * step, y
192
+ dx_, dy_ = nx_ - P[b]["x"], ny_ - P[b]["y"]
193
+ # drag the node's dead-end stubs along -- the branch-conventions pass will
194
+ # re-place them outward, but they must not be STRANDED at the old location
195
+ # (a stranded stub can end up under its own moved parent).
196
+ for t2 in nsucc.get(b, []):
197
+ if t2 not in body and deadend_private(t2, b):
198
+ for m2 in _stub_chain(t2, b):
199
+ P[m2] = {"x": P[m2]["x"] + dx_, "y": P[m2]["y"] + dy_}
200
+ P[b] = {"x": nx_, "y": ny_}
201
+ return P
202
+
203
+
204
+ def _layout_quality(positions: dict, fg) -> tuple:
205
+ """(overlaps, crossings, maxedge) on straight segments -- the strictly-better gate for
206
+ optional post-passes (fan lanes): ties keep the existing output so approved layouts
207
+ can't churn."""
208
+ P = positions
209
+ ids = [i for i in P]
210
+ ov = 0
211
+ for i in range(len(ids)):
212
+ a = P[ids[i]]
213
+ for j in range(i + 1, len(ids)):
214
+ b = P[ids[j]]
215
+ # TRUE intersection only (nodes are 48px): the reference layout deliberately tucks a stub ~50px
216
+ # from a neighbouring lane -- a near-touch must not veto a variant that saves a
217
+ # crossing. (Placement decisions still use the looser 56px `occupied`.)
218
+ if abs(a["x"] - b["x"]) < 48 and abs(a["y"] - b["y"]) < 48:
219
+ ov += 1
220
+ segs = [(P[e.source], P[e.dest]) for e in fg.edges if e.source in P and e.dest in P]
221
+
222
+ def orient(p, q, r):
223
+ return (q["y"] - p["y"]) * (r["x"] - q["x"]) - (q["x"] - p["x"]) * (r["y"] - q["y"])
224
+ cross = 0
225
+ for i in range(len(segs)):
226
+ a, b = segs[i]
227
+ for j in range(i + 1, len(segs)):
228
+ c, d = segs[j]
229
+ pts = {(a["x"], a["y"]), (b["x"], b["y"]), (c["x"], c["y"]), (d["x"], d["y"])}
230
+ if len(pts) < 4:
231
+ continue
232
+ if (orient(a, b, c) > 0) != (orient(a, b, d) > 0) and (orient(c, d, a) > 0) != (orient(c, d, b) > 0):
233
+ cross += 1
234
+ med = max((abs(a["x"] - b["x"]) + abs(a["y"] - b["y"]) for a, b in segs), default=0.0)
235
+ return (ov, cross, round(med))
236
+
237
+
238
+ def _branch_managed_nodes(fg) -> set:
239
+ """Node ids whose placement _apply_branch_conventions owns: the private dead-end chains of
240
+ decisional DEFAULT / conditional branches and error-port branches (mirrors that pass's own
241
+ selection). Clustering must not fold these -- the perpendicular post-pass would re-flatten
242
+ them, wasting the fold and leaving make-room artifacts. default->Join (in-flow) is NOT
243
+ managed, so it stays foldable as part of the spine."""
244
+ from collections import defaultdict
245
+ kind = {n.id: n.shape for n in fg.nodes}
246
+ parent = {n.id: n.parent_id for n in fg.nodes}
247
+ succ: dict = defaultdict(list)
248
+ pred: dict = defaultdict(list)
249
+ for e in fg.edges:
250
+ succ[e.source].append(e.dest)
251
+ pred[e.dest].append(e.source)
252
+
253
+ def private_chain(start, src):
254
+ chain, cur, seen = [start], start, {start, src}
255
+ while True:
256
+ nx = succ.get(cur, [])
257
+ if len(nx) != 1:
258
+ break
259
+ nxt = nx[0]
260
+ if nxt in seen or len(pred.get(nxt, [])) != 1:
261
+ break
262
+ chain.append(nxt)
263
+ seen.add(nxt)
264
+ cur = nxt
265
+ return chain
266
+
267
+ managed: set = set()
268
+ for e in fg.edges:
269
+ s, d = e.source, e.dest
270
+ if parent.get(s) or parent.get(d):
271
+ continue
272
+ is_err = getattr(e, "type", 0) == 1
273
+ is_def = bool(getattr(e, "is_default", False)) and kind.get(s) == "diamond"
274
+ # only DEFAULT + ERROR branches are laid perpendicular-flat; a conditional (non-default)
275
+ # port is usually the in-flow spine continuation, so it stays foldable.
276
+ if not (is_err or is_def):
277
+ continue
278
+ if is_def and kind.get(d) == "circle": # default -> Join stays in-flow (foldable)
279
+ continue
280
+ if len(pred.get(d, [])) != 1:
281
+ continue
282
+ chain = private_chain(d, s)
283
+ if succ.get(chain[-1]): # reconnects (retry loop) -> loop-curl owns it
284
+ continue
285
+ managed.update(chain)
286
+ return managed
287
+
288
+
289
+ def _apply_branch_conventions(positions: dict, fg, fold_long: bool = False) -> dict:
290
+ """The reference branch-placement rule as an ENGINE-AGNOSTIC post-pass (runs on final absolute
291
+ positions, so it applies to either engine's output).
292
+
293
+ An action's **error-port** target, and a decisional's **DEFAULT** target, are placed
294
+ PERPENDICULAR to the flow — the branch's private dead-end chain sits in a lane directly
295
+ above/below the source (first node at the source's x), continuing right along that lane.
296
+ The ONLY exception is a DEFAULT whose target is a **Join** (merge): it stays IN-FLOW. This
297
+ follows the stated rule verbatim ("default … perpendicular … exception … goes to a
298
+ Join") — there is deliberately NO chain-length test (a long default main-path chain still
299
+ drops perpendicular; confirmed by his newest hand-edit). A branch that RECONNECTS (a retry
300
+ loop) is left to the loop-curl. Collision-checked: a branch keeps its engine position if no
301
+ free up/down lane exists. Container (For-Each) members are never moved."""
302
+ from collections import defaultdict
303
+ P = positions
304
+ kind = {n.id: n.shape for n in fg.nodes}
305
+ parent = {n.id: n.parent_id for n in fg.nodes}
306
+ succ: dict = defaultdict(list)
307
+ pred: dict = defaultdict(list)
308
+ for e in fg.edges:
309
+ if e.source in P and e.dest in P:
310
+ succ[e.source].append(e.dest)
311
+ pred[e.dest].append(e.source)
312
+ DX, GAP = 160.0, 180.0
313
+ # main-body centre for the OUTWARD side choice — measured from the SPINE only (nodes that
314
+ # have a successor), NOT all nodes: the terminal Stops we're about to move are scattered
315
+ # up/down and would skew a global median, tipping near-spine sources to the wrong side.
316
+ _spine = sorted(P[k]["y"] for k in P if succ.get(k)) or sorted(v["y"] for v in P.values())
317
+ center_y = _spine[len(_spine) // 2] if _spine else 0.0
318
+
319
+ def occupied(x, y, exclude, thresh=56):
320
+ # default 56 = comfortable non-overlap; the _eval fallback retries at 48 (true node
321
+ # intersection) so the deliberate ~50px stub tucks stay possible as a LAST resort.
322
+ return any(k not in exclude and abs(P[k]["x"] - x) < thresh and abs(P[k]["y"] - y) < thresh
323
+ for k in P)
324
+
325
+ def near_edge(x, y, exclude):
326
+ # a node must never sit ON an edge path (rule: the default lane belongs to the edge).
327
+ for e2 in fg.edges:
328
+ a, b = e2.source, e2.dest
329
+ if a not in P or b not in P or a in exclude or b in exclude:
330
+ continue
331
+ if a in pending_stubs or b in pending_stubs: # stub awaits re-placement: its edge
332
+ continue # position is stale, must not veto
333
+ ax_, ay_, bx_, by_ = P[a]["x"], P[a]["y"], P[b]["x"], P[b]["y"]
334
+ dx_, dy_ = bx_ - ax_, by_ - ay_
335
+ L2 = dx_ * dx_ + dy_ * dy_
336
+ if L2 < 1:
337
+ continue
338
+ u = max(0.0, min(1.0, ((x - ax_) * dx_ + (y - ay_) * dy_) / L2))
339
+ px_, py_ = ax_ + u * dx_, ay_ + u * dy_
340
+ if (x - px_) ** 2 + (y - py_) ** 2 < 28 * 28:
341
+ return True
342
+ return False
343
+
344
+ edges_geo = [(e.source, e.dest) for e in fg.edges if e.source in P and e.dest in P]
345
+
346
+ def _orient(a, b, c):
347
+ return (b["y"] - a["y"]) * (c["x"] - b["x"]) - (b["x"] - a["x"]) * (c["y"] - b["y"])
348
+
349
+ def crossings():
350
+ n = 0
351
+ for i in range(len(edges_geo)):
352
+ s1, d1 = edges_geo[i]
353
+ a, b = P[s1], P[d1]
354
+ for j in range(i + 1, len(edges_geo)):
355
+ s2, d2 = edges_geo[j]
356
+ if len({s1, d1, s2, d2}) < 4:
357
+ continue
358
+ cc, dd = P[s2], P[d2]
359
+ if ((_orient(a, b, cc) > 0) != (_orient(a, b, dd) > 0)
360
+ and (_orient(cc, dd, a) > 0) != (_orient(cc, dd, b) > 0)):
361
+ n += 1
362
+ return n
363
+
364
+ def _lane_coords(ax, sy, sign, n):
365
+ """Perpendicular lane positions for a branch's private chain. Short chains (or when
366
+ clustering is off) run flat along a single lane; a LONG chain (clustering on) FOLDS into
367
+ boustrophedon shelves that stack AWAY from the spine, so a 13-action default tail becomes
368
+ a compact block beside the flow instead of a lane running off-screen."""
369
+ lane = sy + sign * GAP
370
+ W = 4
371
+ if not fold_long or n <= W:
372
+ return [(ax + k * DX, lane) for k in range(n)]
373
+ coords = [None] * n
374
+ for si in range((n + W - 1) // W):
375
+ block = list(range(si * W, min(si * W + W, n)))
376
+ seq = block if si % 2 == 0 else list(reversed(block))
377
+ base = 0 if si % 2 == 0 else (W - len(block)) # right-align R->L shelves: a short
378
+ rowy = lane + sign * si * GAP # remainder drops under the previous
379
+ for ci, k in enumerate(seq): # shelf's END, not back at its start
380
+ coords[k] = (ax + (base + ci) * DX, rowy)
381
+ return coords
382
+
383
+ def _inflow_coords(ax, sy, n):
384
+ """In-flow lane (a conditional dead-end continuing right of the decisional). Short chains
385
+ stay a single flat in-flow row; a LONG one (clustering on) FOLDS into shelves that wrap
386
+ DOWNWARD, so a give-up tail compacts into a block instead of running off the canvas."""
387
+ W = 4
388
+ if not fold_long or n <= W:
389
+ return [(ax + (k + 1) * DX, sy) for k in range(n)]
390
+ coords = [None] * n
391
+ for si in range((n + W - 1) // W):
392
+ block = list(range(si * W, min(si * W + W, n)))
393
+ seq = block if si % 2 == 0 else list(reversed(block))
394
+ base = 0 if si % 2 == 0 else (W - len(block)) # right-align R->L shelves (see above)
395
+ rowy = sy + si * GAP
396
+ for ci, k in enumerate(seq):
397
+ coords[k] = (ax + (base + ci + 1) * DX, rowy)
398
+ return coords
399
+
400
+ def private_chain(start, src):
401
+ chain, cur, seen = [start], start, {start, src}
402
+ while True:
403
+ nx = succ.get(cur, [])
404
+ if len(nx) != 1:
405
+ break
406
+ nxt = nx[0]
407
+ if nxt in seen or len(pred.get(nxt, [])) != 1:
408
+ break
409
+ chain.append(nxt)
410
+ seen.add(nxt)
411
+ cur = nxt
412
+ return chain
413
+
414
+ # collect every stub chain this pass WILL re-place: until a chain is placed, its edges
415
+ # must be invisible to near_edge (an unplaced stub's edge can slice through a good slot).
416
+ pending_stubs: set = set()
417
+ for e in fg.edges:
418
+ s, d = e.source, e.dest
419
+ if s not in P or d not in P or parent.get(s) or parent.get(d):
420
+ continue
421
+ _err = (getattr(e, "type", 0) == 1)
422
+ _def = bool(getattr(e, "is_default", False)) and kind.get(s) == "diamond"
423
+ _cond = kind.get(s) == "diamond" and not _def and not _err
424
+ if not (_err or _def or _cond):
425
+ continue
426
+ if _def and kind.get(d) == "circle":
427
+ continue
428
+ if len(pred.get(d, [])) != 1:
429
+ continue
430
+ ch = private_chain(d, s)
431
+ if not succ.get(ch[-1]):
432
+ pending_stubs.update(ch)
433
+
434
+ for e in fg.edges:
435
+ s, d = e.source, e.dest
436
+ if s not in P or d not in P or parent.get(s) or parent.get(d):
437
+ continue
438
+ is_err = (getattr(e, "type", 0) == 1)
439
+ is_def = bool(getattr(e, "is_default", False)) and kind.get(s) == "diamond"
440
+ # a conditional (non-default / non-error) branch OFF A DECISIONAL
441
+ cond = kind.get(s) == "diamond" and not is_def and not is_err
442
+ if not (is_err or is_def or cond):
443
+ continue
444
+ if is_def and kind.get(d) == "circle": # default -> Join: stays in-flow
445
+ continue
446
+ if len(pred.get(d, [])) != 1: # target must be a private branch head
447
+ continue
448
+ chain = private_chain(d, s)
449
+ if succ.get(chain[-1]): # reconnects (retry loop) -> leave it
450
+ continue
451
+ pending_stubs.difference_update(chain) # being placed now -> edges become real
452
+ ax, sy, excl = P[s]["x"], P[s]["y"], set(chain)
453
+ # A CONDITIONAL dead-end branch of a decisional continues IN-FLOW (right of the
454
+ # decisional) when that lane is free — the flow keeps moving right, with the
455
+ # perpendicular lanes reserved for the default + loop branches (the rule: a
456
+ # non-default terminal like a give-up Stop goes in-flow if the default didn't take it).
457
+ def _eval(cands):
458
+ exclS = excl | {s}
459
+ for thresh in (56, 48): # tight 48 tuck only when nothing clears 56
460
+ best, best_cc = None, None
461
+ for cand in cands:
462
+ if any(occupied(px, py, excl, thresh) for px, py in cand):
463
+ continue
464
+ if any(near_edge(px, py, exclS) for px, py in cand):
465
+ continue
466
+ snap = {nid: dict(P[nid]) for nid in chain}
467
+ for nid, (px, py) in zip(chain, cand):
468
+ P[nid] = {"x": px, "y": py}
469
+ cc = crossings()
470
+ for nid in chain:
471
+ P[nid] = snap[nid]
472
+ if best is None or cc < best_cc: # candidates ordered: ties keep the FIRST
473
+ best, best_cc = cand, cc
474
+ if best is not None:
475
+ return best
476
+ return None
477
+
478
+ n = len(chain)
479
+
480
+ def _vcol(x_first, y_first, sign, n_):
481
+ """Vertical serpentine for a dead-end chain: columns of <=4 stepping away from the
482
+ flow (sign), folding into the next column when deeper -- reference chains run
483
+ vertically but never taller than ~4 rows (his ANAF pocket is 2 columns x 4)."""
484
+ W = 4
485
+ cols = [list(range(k, min(k + W, n_))) for k in range(0, n_, W)]
486
+ out = [None] * n_
487
+ base = len(cols[0]) - 1
488
+ for j, col in enumerate(cols):
489
+ x = x_first + j * DX
490
+ for r, idx in enumerate(col):
491
+ row = r if j % 2 == 0 else base - r
492
+ out[idx] = (x, y_first + sign * row * GAP)
493
+ return out
494
+
495
+ # Reference pattern: a LONG dead-end chain runs VERTICALLY (constant x, stepping away
496
+ # from the flow) -- a compact column instead of a lane running off-canvas (his
497
+ # GetInfoSales terminal chain and 0GetInfo stubs). Short chains (1-2) keep the
498
+ # approved L-shape lane. Vertical goes FIRST for n>=3 so it wins crossing ties.
499
+ # the DEFAULT owns the in-flow lane: when this decisional's default edge continues
500
+ # in-flow (default -> Join stays in-flow per the stated exception), a conditional
501
+ # dead-end must NOT take that slot -- it goes perpendicular instead (his 0GetInfo
502
+ # fix: the no-info Stop drops below the decisional; the default runs on to the Join).
503
+ default_inflow = any(bool(getattr(e2, "is_default", False)) and kind.get(e2.dest) == "circle"
504
+ for e2 in fg.edges if e2.source == s)
505
+ if cond and not default_inflow:
506
+ flat = _inflow_coords(ax, sy, n)
507
+ drop = _vcol(ax + DX, sy, 1, n) # head in-flow, then DOWN
508
+ best = _eval([drop, flat] if n >= 3 else [flat])
509
+ if best is not None:
510
+ for nid, (px, py) in zip(chain, best):
511
+ P[nid] = {"x": px, "y": py}
512
+ continue
513
+ # outward = AWAY from where the flow came from: a fan-lane branch above the spine
514
+ # sends its stub further up, one below sends it down (the 0GetInfo golden). When
515
+ # the source sits in-line with its predecessors (a spine node), fall back to the
516
+ # global centre -- which keeps every previously-approved placement unchanged.
517
+ preds_y = sorted(P[p]["y"] for p in pred.get(s, []) if p in P)
518
+ pmed = preds_y[len(preds_y) // 2] if preds_y else None
519
+ if pmed is not None and abs(sy - pmed) > GAP * 0.5:
520
+ outward = 1 if sy > pmed else -1
521
+ else:
522
+ outward = 1 if sy >= center_y else -1 # perpendicular, crossing-driven side
523
+ cands = []
524
+ for sign in (outward, -outward): # outward tried first => ties keep outward
525
+ lane = _lane_coords(ax, sy, sign, n)
526
+ vert = _vcol(ax, sy + sign * GAP, sign, n)
527
+ cands += [vert, lane] if n >= 3 else [lane, vert]
528
+ if n == 1: # the reference half-gap tuck for a lone stub
529
+ cands.append([(ax, sy + sign * GAP * 0.72)])
530
+ best = _eval(cands)
531
+ if best:
532
+ for nid, (px, py) in zip(chain, best):
533
+ P[nid] = {"x": px, "y": py}
534
+ return positions
535
+
536
+
537
+ def layout_flow(obj: dict, flow_id: str | None = None,
538
+ only: list[str] | None = None, cluster: bool = False,
539
+ minimize_crossings: bool = False) -> dict:
540
+ """Re-lay-out one flow. Returns {flow_id, nodes_moved, areas_resized, bbox, positions,
541
+ areas, bundle} where `bundle` is the modified copy of `obj`.
542
+
543
+ `only` = a list of action ids to reposition WITHOUT reshuffling the rest (the
544
+ "re-tidy just this action" mode). In that mode only those actions' positions are
545
+ written; every other action, the area frames, and CanvasData are left byte-identical."""
546
+ import os
547
+ if (os.environ.get("LAYOUT_ENGINE") or "").strip().lower() == "auto":
548
+ return _best_of_engines(obj, flow_id=flow_id, only=only)
549
+ obj = copy.deepcopy(obj)
550
+ flows, _fk = _flows(obj)
551
+ if flows is not None:
552
+ if flow_id:
553
+ flow = next((f for f in flows if _get(f, "Id") == flow_id), None)
554
+ if flow is None:
555
+ raise ValueError(f"flow_id {flow_id} not found")
556
+ elif len(flows) == 1:
557
+ flow = flows[0]
558
+ else:
559
+ raise ValueError(f"bundle has {len(flows)} flows; pass flow_id")
560
+ else:
561
+ flow = obj
562
+
563
+ fg = read_flow(flow)
564
+ nodes = [{"id": n.id, "width": n.size["w"], "height": n.size["h"],
565
+ "parent_id": n.parent_id, "kind": n.shape, "position": n.position}
566
+ for n in fg.nodes]
567
+ edges = [{"source": e.source, "dest": e.dest, "type": e.type,
568
+ "is_default": e.is_default} for e in fg.edges]
569
+ only_set = set(only) if only else None
570
+ if only_set is not None:
571
+ missing = only_set - {n.id for n in fg.nodes}
572
+ if missing:
573
+ raise ValueError(f"--only action id(s) not in this flow: {sorted(missing)}")
574
+ opts = {}
575
+ if only_set is None:
576
+ if cluster:
577
+ opts["CLUSTER"] = True
578
+ opts["NO_CLUSTER_IDS"] = _branch_managed_nodes(fg)
579
+ if minimize_crossings:
580
+ opts["MINIMIZE_CROSSINGS"] = True
581
+ # Single dispatch point: dispatch.layout picks the engine from the LAYOUT_ENGINE flag
582
+ # (legacy default, elk opt-in). Both engines share this exact signature + return contract,
583
+ # so everything below (designer_writes, Start-anchoring, write-back) is engine-agnostic.
584
+ # Cycle-aware: a large embedded cycle (SCC) is collapsed to a meta-node, laid out around,
585
+ # then expanded as a compact band; falls through to a plain dispatch otherwise.
586
+ result = cycles.layout(nodes, edges, opts=(opts or None),
587
+ subset=(list(only_set) if only_set else None),
588
+ dispatch_layout=dispatch.layout)
589
+ positions, areas = result["positions"], result["areas"]
590
+
591
+ # Engine-agnostic branch-placement convention (the reference rule): error-port + decisional
592
+ # default branches drop perpendicular to the flow (unless default->Join). Full layouts only.
593
+ if only_set is None and positions:
594
+ base_pos = _apply_branch_conventions({k: dict(v) for k, v in positions.items()},
595
+ fg, fold_long=cluster)
596
+ # FAN pattern (parallel alternative branches between one split and one merge -> flat
597
+ # stacked lanes). Tried as a variant and kept ONLY when strictly better.
598
+ fan_pos = _apply_fan_lanes({k: dict(v) for k, v in positions.items()}, fg)
599
+ if fan_pos != positions:
600
+ fan_pos = _apply_branch_conventions(fan_pos, fg, fold_long=cluster)
601
+ positions = fan_pos if _layout_quality(fan_pos, fg) < _layout_quality(base_pos, fg) else base_pos
602
+ else:
603
+ positions = base_pos
604
+
605
+ actions = _get(flow, "Actions", default=[]) or []
606
+ # Anchor a full re-layout to where the diagram already sits — keep Start exactly put —
607
+ # so the user's designer viewport still frames it. Without this the whole graph jumps to
608
+ # the origin and the canvas looks empty until you pan/fit far away.
609
+ if only_set is None and positions:
610
+ old = {}
611
+ for a in actions:
612
+ aid = _get(a, "Id")
613
+ p = _get(_get(a, "CustomData", default={}) or {}, "position")
614
+ if aid and isinstance(p, dict) and p.get("x") is not None:
615
+ old[aid] = {"x": float(p["x"]), "y": float(p["y"])}
616
+ anchor = fg.start_id if (fg.start_id in old and fg.start_id in positions) else None
617
+ if anchor:
618
+ sx = old[anchor]["x"] - positions[anchor]["x"]
619
+ sy = old[anchor]["y"] - positions[anchor]["y"]
620
+ elif old:
621
+ sx = min(p["x"] for p in old.values()) - min(p["x"] for p in positions.values())
622
+ sy = min(p["y"] for p in old.values()) - min(p["y"] for p in positions.values())
623
+ else:
624
+ sx = sy = 0.0
625
+ if sx or sy:
626
+ positions = {k: {"x": v["x"] + sx, "y": v["y"] + sy} for k, v in positions.items()}
627
+ areas = {k: {**v, "x": v["x"] + sx, "y": v["y"] + sy} for k, v in areas.items()}
628
+
629
+ # PROCESIO stores a container child's position RELATIVE to its parent area's frame,
630
+ # and an area node's position IS that frame top-left. engine.designer_writes encodes it.
631
+ parent_of = {_get(a, "Id"): _get(a, "ParentId") for a in actions
632
+ if _get(a, "ParentId") in areas}
633
+ writes = engine.designer_writes(positions, areas, parent_of)
634
+
635
+ moved = resized = 0
636
+ for a in actions:
637
+ aid = _get(a, "Id")
638
+ if only_set is not None and aid not in only_set:
639
+ continue # partial mode: leave the rest untouched
640
+ w = writes.get(aid)
641
+ if not w:
642
+ continue
643
+ cd_key = _key(a, "CustomData")
644
+ if not cd_key:
645
+ continue
646
+ cd = a[cd_key]
647
+ pos_key = _key(cd, "position") or "position"
648
+ cd[pos_key] = dict(w["pos"])
649
+ moved += 1
650
+ if w["areaSize"] is not None:
651
+ asz_key = _key(cd, "areaSize") or "areaSize"
652
+ cd[asz_key] = dict(w["areaSize"])
653
+ resized += 1
654
+ # NEVER touch CanvasData (the designer viewport pan/zoom). Resetting it jumps the
655
+ # user's view away from the content — leave it exactly as the flow had it; the user
656
+ # can fit-to-screen in the designer to frame a re-laid-out process.
657
+ return {"flow_id": _get(flow, "Id"), "nodes_moved": moved,
658
+ "areas_resized": resized, "bbox": result["bbox"],
659
+ "positions": {k: _round(v) for k, v in positions.items()},
660
+ "areas": areas, "bundle": obj}
661
+
662
+
663
+ def verify_render(obj: dict, flow_id: str | None = None) -> dict:
664
+ """Simulate how the PROCESIO designer renders a flow's CURRENT positions (a container
665
+ child's stored position is RELATIVE to its parent area's frame top-left) and report
666
+ layout problems: children rendered outside their container frame, and node overlaps.
667
+ Read-only — does not modify the flow. Returns {ok, issue_count, issues, areas, nodes}."""
668
+ flows, _fk = _flows(obj)
669
+ if flows is not None:
670
+ flow = (next((f for f in flows if _get(f, "Id") == flow_id), None) if flow_id
671
+ else (flows[0] if len(flows) == 1 else None))
672
+ if flow is None:
673
+ raise ValueError("verify_render: bundle has multiple flows; pass flow_id")
674
+ else:
675
+ flow = obj
676
+ actions = _get(flow, "Actions", default=[]) or []
677
+ nodes = {}
678
+ for a in actions:
679
+ aid = _get(a, "Id")
680
+ cd = _get(a, "CustomData", default={}) or {}
681
+ p = _get(cd, "position") or {}
682
+ asz = _get(cd, "areaSize") or {}
683
+ is_area = _get(cd, "type") == "area"
684
+ nodes[aid] = {
685
+ "x": float(p.get("x", 0) or 0), "y": float(p.get("y", 0) or 0),
686
+ "parent": _get(a, "ParentId"), "is_area": is_area,
687
+ "w": float(asz.get("width", 48) or 48) if is_area else 48.0,
688
+ "h": float(asz.get("height", 48) or 48) if is_area else 48.0,
689
+ "name": _get(cd, "name") or _get(a, "ActionTemplateName") or aid,
690
+ }
691
+ # absolute rendered position: a child is offset by its parent area's absolute top-left
692
+ absol = {}
693
+ for aid, n in nodes.items():
694
+ par = n["parent"]
695
+ if par in nodes and nodes[par]["is_area"]:
696
+ absol[aid] = {"x": nodes[par]["x"] + n["x"], "y": nodes[par]["y"] + n["y"]}
697
+ else:
698
+ absol[aid] = {"x": n["x"], "y": n["y"]}
699
+ issues = []
700
+ for aid, n in nodes.items():
701
+ par = n["parent"]
702
+ if par in nodes and nodes[par]["is_area"]:
703
+ pp = nodes[par]
704
+ cx, cy = absol[aid]["x"], absol[aid]["y"]
705
+ if not (pp["x"] - 1 <= cx - 24 and cx + 24 <= pp["x"] + pp["w"] + 1
706
+ and pp["y"] - 1 <= cy - 24 and cy + 24 <= pp["y"] + pp["h"] + 1):
707
+ issues.append({"type": "child_outside_frame", "child": n["name"], "area": pp["name"]})
708
+ leaves = [aid for aid, n in nodes.items() if not n["is_area"]]
709
+ for i in range(len(leaves)):
710
+ a = absol[leaves[i]]
711
+ for j in range(i + 1, len(leaves)):
712
+ b = absol[leaves[j]]
713
+ if abs(a["x"] - b["x"]) < 46 and abs(a["y"] - b["y"]) < 46:
714
+ issues.append({"type": "overlap",
715
+ "a": nodes[leaves[i]]["name"], "b": nodes[leaves[j]]["name"]})
716
+ return {"ok": not issues, "issue_count": len(issues), "issues": issues[:40],
717
+ "areas": sum(1 for n in nodes.values() if n["is_area"]), "nodes": len(nodes)}
718
+
719
+
720
+ def layout_resource_map(obj: dict, root_flow_id: str | None = None,
721
+ levels: int | None = None) -> dict:
722
+ """Position the cross-process (process→process) call graph. Returns
723
+ {root, nodes:[{flow_id, title, x, y}], edges:[...]}. Callers fall left of their
724
+ callees (longest-path layering); LVL-centering on the root is a future refinement."""
725
+ rm = read_bundle(obj)
726
+ graphs = rm["flows"]
727
+ proc_edges = rm["process_edges"]
728
+ node_ids = set(graphs) | {e["target"] for e in proc_edges} | {e["source"] for e in proc_edges}
729
+ nodes = [{"id": fid, "width": 220, "height": 60} for fid in sorted(node_ids)]
730
+ edges = [{"source": e["source"], "dest": e["target"], "type": 0} for e in proc_edges]
731
+ result = engine.layout(nodes, edges, {"DX": 360, "DY": 90, "NODE_W": 220, "NODE_H": 60})
732
+ titles = {fid: (g.get("title") if isinstance(g, dict) else None) for fid, g in graphs.items()}
733
+ out_nodes = [{"flow_id": fid, "title": titles.get(fid),
734
+ "x": p["x"], "y": p["y"]} for fid, p in sorted(result["positions"].items())]
735
+ return {"root": root_flow_id, "nodes": out_nodes, "edges": proc_edges,
736
+ "bbox": result["bbox"]}