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,147 @@
1
+ """Named connection-profile store shared by the `sqlserver` and `mysql` tools.
2
+
3
+ Split storage, by design (see CLAUDE.md Hard rule 1 — no secrets in files):
4
+
5
+ * NON-SECRET config (profile name, server/host, port, database, username,
6
+ driver, TLS flags, timeouts) lives in a ``profiles.json`` file INSIDE the
7
+ tool directory. It never contains a password.
8
+ * The PASSWORD lives ONLY in Windows Credential Manager, under the target
9
+ ``agents-and-tools:<tool>:<profile-name>`` — i.e. the secret name IS the
10
+ profile name. Read at runtime via ``tools._lib.creds.get(tool, profile)``.
11
+
12
+ So a profile named ``default`` on the ``sqlserver`` tool keeps its host/db/etc.
13
+ in ``tools/sqlserver/profiles.json`` and its password under the credential
14
+ target ``agents-and-tools:sqlserver:default``. Set it with:
15
+
16
+ python scripts/set-credential.py sqlserver default
17
+
18
+ This module is a thin, dependency-light wrapper (only stdlib + creds). It does
19
+ not import any DB driver, so it stays unit-testable and import-safe.
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import json
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ from tools._lib import creds
28
+
29
+
30
+ class ProfileError(Exception):
31
+ """Bad profile usage (unknown name, duplicate, missing field). -> invalid_argument."""
32
+
33
+
34
+ class AuthRequiredError(Exception):
35
+ """A profile exists but its password is not in Credential Manager. -> auth_required."""
36
+
37
+
38
+ class ProfileStore:
39
+ """Reads/writes the per-tool ``profiles.json`` and resolves passwords.
40
+
41
+ Parameters
42
+ ----------
43
+ tool : str
44
+ The tool name (``"sqlserver"`` or ``"mysql"``). Used as the Credential
45
+ Manager namespace AND must match the manifest ``name``.
46
+ path : Path
47
+ Absolute path to this tool's ``profiles.json``.
48
+ """
49
+
50
+ def __init__(self, tool: str, path: Path):
51
+ self.tool = tool
52
+ self.path = Path(path)
53
+
54
+ # -- raw file I/O -------------------------------------------------------
55
+
56
+ def _read(self) -> dict[str, dict]:
57
+ if not self.path.exists():
58
+ return {}
59
+ try:
60
+ data = json.loads(self.path.read_text(encoding="utf-8"))
61
+ except (json.JSONDecodeError, OSError):
62
+ return {}
63
+ return data if isinstance(data, dict) else {}
64
+
65
+ def _write(self, data: dict[str, dict]) -> None:
66
+ self.path.parent.mkdir(parents=True, exist_ok=True)
67
+ self.path.write_text(
68
+ json.dumps(data, indent=2, ensure_ascii=False, sort_keys=True) + "\n",
69
+ encoding="utf-8",
70
+ )
71
+
72
+ # -- CRUD ---------------------------------------------------------------
73
+
74
+ def names(self) -> list[str]:
75
+ return sorted(self._read().keys())
76
+
77
+ def exists(self, name: str) -> bool:
78
+ return name in self._read()
79
+
80
+ def get(self, name: str) -> dict:
81
+ """Return one profile's NON-SECRET config dict (raises if unknown)."""
82
+ data = self._read()
83
+ if name not in data:
84
+ known = ", ".join(sorted(data)) or "(none)"
85
+ raise ProfileError(
86
+ f"no profile named '{name}'. Known: {known}. "
87
+ f"Add one with: run-tool {self.tool} add-profile --name {name} ..."
88
+ )
89
+ blob = dict(data[name])
90
+ blob["name"] = name
91
+ return blob
92
+
93
+ def add(self, name: str, config: dict[str, Any], *, overwrite: bool = False) -> dict:
94
+ """Store a NON-SECRET profile config. Strips any password-like field."""
95
+ if not name:
96
+ raise ProfileError("profile --name is required")
97
+ data = self._read()
98
+ if name in data and not overwrite:
99
+ raise ProfileError(
100
+ f"profile '{name}' already exists. Pass --overwrite to replace it."
101
+ )
102
+ clean = {
103
+ k: v for k, v in config.items()
104
+ if v is not None and k not in ("name", "password", "pwd", "pass")
105
+ }
106
+ data[name] = clean
107
+ self._write(data)
108
+ view = dict(clean)
109
+ view["name"] = name
110
+ view["has_password"] = creds.has_for_report(self.tool, name)
111
+ return view
112
+
113
+ def remove(self, name: str) -> None:
114
+ data = self._read()
115
+ if name not in data:
116
+ raise ProfileError(f"no profile named '{name}'")
117
+ del data[name]
118
+ self._write(data)
119
+
120
+ # -- password (Credential Manager only) ---------------------------------
121
+
122
+ def password(self, name: str) -> str:
123
+ """Fetch the profile password from Credential Manager.
124
+
125
+ Raises ``AuthRequiredError`` (mapped to ``auth_required``) when absent,
126
+ pointing the user at ``scripts/set-credential.py``.
127
+ """
128
+ pw = creds.get_optional(self.tool, name)
129
+ if pw is None:
130
+ raise AuthRequiredError(
131
+ f"no password stored for profile '{name}'. Store it with:\n"
132
+ f" python scripts/set-credential.py {self.tool} {name}\n"
133
+ f"(saved to Windows Credential Manager target "
134
+ f"agents-and-tools:{self.tool}:{name})"
135
+ )
136
+ return pw
137
+
138
+ # -- public, password-free views ---------------------------------------
139
+
140
+ def public_view(self, name: str) -> dict:
141
+ """A profile dict safe to return/log: no password, presence flagged."""
142
+ blob = self.get(name)
143
+ blob["has_password"] = creds.has_for_report(self.tool, name)
144
+ return blob
145
+
146
+ def list_public(self) -> list[dict]:
147
+ return [self.public_view(n) for n in self.names()]
@@ -0,0 +1,286 @@
1
+ """Engine-agnostic SQL helpers shared by the `sqlserver` and `mysql` tools.
2
+
3
+ PURE module: it imports no database driver and opens no connection, so every
4
+ function here is unit-testable with zero infrastructure. The two tools wire it
5
+ to pyodbc / pymysql at the edges.
6
+
7
+ What lives here:
8
+ - ``assert_read_only`` / ``is_write_sql`` — a regex write-statement guard that
9
+ blocks INSERT/UPDATE/DELETE/MERGE/UPSERT/TRUNCATE/ALTER/CREATE/DROP/GRANT/
10
+ EXEC/CALL/... and multi-statement batches, unless ``write=True``.
11
+ - ``rows_to_dicts`` — DBAPI cursor rows -> ``list[dict]`` with JSON-safe
12
+ coercion (datetime/date/time/timedelta/Decimal/bytes/UUID/memoryview/...).
13
+ - ``cap_rows`` — apply a max-rows cap and report a ``truncated`` flag.
14
+ - ``mask_dsn`` — redact a password inside any connection string for logging.
15
+
16
+ The guard is intentionally conservative: it strips string literals, quoted
17
+ identifiers and comments first, then looks for write keywords as whole words at
18
+ statement level. It is a guard, not a SQL parser — the real read-only guarantee
19
+ for SQL Server also leans on ApplicationIntent=ReadOnly + a read-only login when
20
+ configured; for MySQL (no ApplicationIntent equivalent) THIS guard is the
21
+ enforcement, so its keyword list is the superset of both engines.
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import datetime as _dt
26
+ import decimal as _decimal
27
+ import re
28
+ import uuid as _uuid
29
+ from typing import Any, Sequence
30
+
31
+
32
+ class WriteGuardError(Exception):
33
+ """Raised when a read-only query receives a write/DDL/multi-statement SQL.
34
+
35
+ Tools map this to the framework error code ``write_blocked`` (exit 2).
36
+ """
37
+
38
+
39
+ # Write / schema-change / side-effecting keywords. Superset across SQL Server
40
+ # and MySQL so the single guard protects both engines:
41
+ # - DML: INSERT, UPDATE, DELETE, MERGE, REPLACE (MySQL upsert), UPSERT
42
+ # - DDL: CREATE, ALTER, DROP, TRUNCATE, RENAME
43
+ # - permissions: GRANT, REVOKE, DENY
44
+ # - procedural: EXEC, EXECUTE, CALL
45
+ # - bulk / locks: BULK, LOAD, LOCK, UNLOCK, HANDLER, DO
46
+ # - tx / backup: BACKUP, RESTORE, COMMIT, ROLLBACK, SET, USE
47
+ # SET/USE/COMMIT/ROLLBACK are blocked because they change session/tx state and
48
+ # have no place in a read-only ad-hoc query path.
49
+ WRITE_KEYWORDS: tuple[str, ...] = (
50
+ "INSERT", "UPDATE", "DELETE", "MERGE", "UPSERT", "REPLACE",
51
+ "CREATE", "ALTER", "DROP", "TRUNCATE", "RENAME",
52
+ "GRANT", "REVOKE", "DENY",
53
+ "EXEC", "EXECUTE", "CALL",
54
+ "BULK", "LOAD", "LOCK", "UNLOCK", "HANDLER", "DO",
55
+ "BACKUP", "RESTORE", "COMMIT", "ROLLBACK", "SET", "USE",
56
+ )
57
+
58
+ _WRITE_KW_RE = re.compile(r"\b(" + "|".join(WRITE_KEYWORDS) + r")\b", re.IGNORECASE)
59
+
60
+ # Read-only DDL-inspection idioms whose CREATE keyword does not create anything.
61
+ _SHOW_CREATE_RE = re.compile(
62
+ r"\bSHOW\s+CREATE\s+"
63
+ r"(?:TABLE|VIEW|PROCEDURE|FUNCTION|TRIGGER|EVENT|DATABASE|SCHEMA)\b",
64
+ re.IGNORECASE,
65
+ )
66
+
67
+ # Regexes that strip out non-statement text before keyword scanning so that a
68
+ # keyword inside a literal / identifier / comment never trips the guard.
69
+ _STRIP_RES: tuple[re.Pattern[str], ...] = (
70
+ re.compile(r"'(?:[^'\\]|\\.|'')*'", re.DOTALL), # 'single-quoted' ('' or \' escaped)
71
+ re.compile(r'"(?:[^"\\]|\\.|"")*"', re.DOTALL), # "double-quoted"
72
+ re.compile(r"`(?:[^`\\]|\\.)*`", re.DOTALL), # `backtick identifier` (MySQL)
73
+ re.compile(r"\[[^\]]*\]"), # [bracketed identifier] (T-SQL)
74
+ re.compile(r"/\*.*?\*/", re.DOTALL), # /* block comment */
75
+ re.compile(r"--[^\n]*"), # -- line comment
76
+ re.compile(r"#[^\n]*"), # # line comment (MySQL)
77
+ _SHOW_CREATE_RE, # SHOW CREATE ... (read-only)
78
+ )
79
+
80
+
81
+ def _strip_noise(sql: str) -> str:
82
+ """Remove literals, quoted identifiers, comments, SHOW CREATE prefixes."""
83
+ out = sql
84
+ for rx in _STRIP_RES:
85
+ out = rx.sub(" ", out)
86
+ return out
87
+
88
+
89
+ def _split_statements(stripped: str) -> list[str]:
90
+ """Split a noise-stripped SQL on semicolons into non-empty statements.
91
+
92
+ Operates on already-stripped text, so semicolons inside literals/comments
93
+ are gone and can't create phantom statements.
94
+ """
95
+ return [s.strip() for s in stripped.split(";") if s.strip()]
96
+
97
+
98
+ def find_write_keywords(sql: str) -> list[str]:
99
+ """Return write keywords found at statement level (after noise-stripping)."""
100
+ stripped = _strip_noise(sql)
101
+ return [m.group(1).upper() for m in _WRITE_KW_RE.finditer(stripped)]
102
+
103
+
104
+ def is_multi_statement(sql: str) -> bool:
105
+ """True if the SQL is a batch of more than one statement (post-strip)."""
106
+ return len(_split_statements(_strip_noise(sql))) > 1
107
+
108
+
109
+ def is_write_sql(sql: str) -> bool:
110
+ """True if `sql` would be rejected under read-only rules (write or batch)."""
111
+ return bool(find_write_keywords(sql)) or is_multi_statement(sql)
112
+
113
+
114
+ def assert_read_only(sql: str, *, write: bool = False) -> None:
115
+ """Raise ``WriteGuardError`` unless `sql` is a single read-only statement.
116
+
117
+ When ``write=True`` the guard is bypassed entirely (the tool has already
118
+ confirmed the caller passed ``--write``). When ``write=False`` (default),
119
+ any write/DDL/EXEC keyword OR a multi-statement batch is rejected.
120
+ """
121
+ if write:
122
+ return
123
+ hits = find_write_keywords(sql)
124
+ if hits:
125
+ # de-duplicate but keep order for a stable message
126
+ seen: list[str] = []
127
+ for h in hits:
128
+ if h not in seen:
129
+ seen.append(h)
130
+ raise WriteGuardError(
131
+ f"read-only guard blocked write keyword(s) {seen}. "
132
+ f"Pass --write to allow a non-SELECT statement."
133
+ )
134
+ if is_multi_statement(sql):
135
+ raise WriteGuardError(
136
+ "read-only guard blocked a multi-statement batch (';' separates "
137
+ "statements). Run one statement per query, or pass --write."
138
+ )
139
+
140
+
141
+ # ---------------------------------------------------------------------------
142
+ # Line-ending normalization for DDL / script text
143
+ # ---------------------------------------------------------------------------
144
+
145
+ def normalize_eol(sql: str, style: str = "crlf") -> str:
146
+ """Normalize every line ending in `sql` to one consistent style.
147
+
148
+ Collapses CRLF and lone CR to LF first, then expands to the target style,
149
+ so the result never contains a mixed CR/LF run. Default ``"crlf"`` matches
150
+ SQL Server / SSMS: when a ``CREATE``/``ALTER`` of a PROCEDURE, VIEW,
151
+ FUNCTION or TRIGGER is sent with LF-only newlines, SQL Server stores the
152
+ object source verbatim, and SSMS then reports *"Inconsistent Line Endings"*
153
+ on script-out because it prepends a CRLF ``SET ANSI_NULLS…GO`` header.
154
+ Emitting CRLF keeps stored definitions internally consistent.
155
+
156
+ T-SQL is whitespace-insensitive between tokens, so this never changes query
157
+ semantics *outside* of string literals. The one case to preserve a bare LF
158
+ is inside a string literal that must stay LF — callers expose an opt-out
159
+ (the sqlserver tool's ``--no-normalize-eol``) for that. Parameter values are
160
+ bound separately (``?`` placeholders) and are never passed through here.
161
+ """
162
+ lf = sql.replace("\r\n", "\n").replace("\r", "\n")
163
+ if style == "lf":
164
+ return lf
165
+ if style != "crlf":
166
+ raise ValueError(f"unknown eol style: {style!r} (use 'crlf' or 'lf')")
167
+ return lf.replace("\n", "\r\n")
168
+
169
+
170
+ # ---------------------------------------------------------------------------
171
+ # Row formatting: DBAPI rows -> JSON-safe list[dict]
172
+ # ---------------------------------------------------------------------------
173
+
174
+ def json_safe(value: Any) -> Any:
175
+ """Coerce one DBAPI value into something ``json.dumps`` can serialise.
176
+
177
+ datetime/date/time -> ISO 8601 string; timedelta -> total seconds (float);
178
+ Decimal -> str (preserves precision); bytes/bytearray/memoryview -> hex str;
179
+ UUID -> str. Primitives (str/int/float/bool/None) pass through unchanged.
180
+ Anything else falls back to ``str(value)``.
181
+ """
182
+ if value is None or isinstance(value, (str, int, float, bool)):
183
+ return value
184
+ if isinstance(value, (_dt.datetime, _dt.date, _dt.time)):
185
+ return value.isoformat()
186
+ if isinstance(value, _dt.timedelta):
187
+ return value.total_seconds()
188
+ if isinstance(value, _decimal.Decimal):
189
+ return str(value)
190
+ if isinstance(value, (bytes, bytearray)):
191
+ return bytes(value).hex()
192
+ if isinstance(value, memoryview):
193
+ return value.tobytes().hex()
194
+ if isinstance(value, _uuid.UUID):
195
+ return str(value)
196
+ return str(value)
197
+
198
+
199
+ def column_names(cursor) -> list[str]:
200
+ """Column names from a DBAPI ``cursor.description`` (empty if no result set)."""
201
+ if not getattr(cursor, "description", None):
202
+ return []
203
+ return [d[0] for d in cursor.description]
204
+
205
+
206
+ def first_result_columns(cursor) -> list[str]:
207
+ """Columns of the first result that has any, stepping past row-count-only results.
208
+
209
+ A multi-statement batch run without SET NOCOUNT ON opens with "N rows affected"
210
+ results that carry no columns; the rows the caller wants come after them.
211
+ """
212
+ columns = column_names(cursor)
213
+ nextset = getattr(cursor, "nextset", None)
214
+ while not columns and nextset is not None and nextset():
215
+ columns = column_names(cursor)
216
+ return columns
217
+
218
+
219
+ def drain_results(cursor) -> None:
220
+ """Step through every remaining result of the last ``execute`` so its errors surface.
221
+
222
+ The server returns a batch's results in order and the driver raises a statement's
223
+ error only when the cursor reaches it. A batch whose INSERT reported "1 row affected"
224
+ and whose next statement failed looks successful until ``nextset()`` gets there, so
225
+ a caller that must not lose an error drains after every execute.
226
+ """
227
+ nextset = getattr(cursor, "nextset", None)
228
+ if nextset is None: # test doubles without result sets
229
+ return
230
+ while nextset():
231
+ pass
232
+
233
+
234
+ def rows_to_dicts(
235
+ columns: Sequence[str],
236
+ raw_rows: Sequence[Any],
237
+ ) -> list[dict]:
238
+ """Map DBAPI rows to JSON-safe dicts keyed by `columns`.
239
+
240
+ Accepts both sequence rows (pyodbc tuples) and mapping rows (pymysql
241
+ DictCursor dicts) — for a dict row we read each column by name, for a
242
+ sequence row by position. Every value is passed through ``json_safe``.
243
+ """
244
+ out: list[dict] = []
245
+ for row in raw_rows:
246
+ if isinstance(row, dict):
247
+ out.append({c: json_safe(row.get(c)) for c in columns})
248
+ else:
249
+ out.append({columns[i]: json_safe(row[i]) for i in range(len(columns))})
250
+ return out
251
+
252
+
253
+ def cap_rows(rows: list[Any], max_rows: int | None) -> tuple[list[Any], bool]:
254
+ """Trim `rows` to at most `max_rows`; return ``(rows, truncated)``.
255
+
256
+ ``max_rows is None`` or ``<= 0`` means no cap. ``truncated`` is True only
257
+ when rows were actually dropped.
258
+ """
259
+ if max_rows is None or max_rows <= 0:
260
+ return rows, False
261
+ if len(rows) > max_rows:
262
+ return rows[:max_rows], True
263
+ return rows, False
264
+
265
+
266
+ # ---------------------------------------------------------------------------
267
+ # Connection-string masking (for safe logging / test-connection output)
268
+ # ---------------------------------------------------------------------------
269
+
270
+ # pyodbc uses PWD=...; pymysql-style DSNs may use password=...; URL DSNs use
271
+ # scheme://user:password@host. Cover all three.
272
+ _MASK_RES: tuple[tuple[re.Pattern[str], str], ...] = (
273
+ (re.compile(r"(?i)(PWD\s*=)[^;]*"), r"\1***"),
274
+ (re.compile(r"(?i)(PASSWORD\s*=)[^;]*"), r"\1***"),
275
+ (re.compile(r"(?i)(PASSWORD\s*:\s*)[^,}\s]+"), r"\1***"),
276
+ # scheme://user:password@host -> scheme://user:***@host
277
+ (re.compile(r"(://[^:/@\s]+:)[^@/\s]+(@)"), r"\1***\2"),
278
+ )
279
+
280
+
281
+ def mask_dsn(dsn: str) -> str:
282
+ """Replace any password value in a connection string with ``***``."""
283
+ out = dsn
284
+ for rx, repl in _MASK_RES:
285
+ out = rx.sub(repl, out)
286
+ return out
@@ -0,0 +1,225 @@
1
+ """Engine-agnostic schema-mirror, diff, and migration helpers.
2
+
3
+ Shared by the `sqlserver` and `mysql` tools' `schema-extract`, `schema-diff`, and
4
+ `migrate` actions. PURE: no DB driver import, no network — every function is a
5
+ deterministic transform of its inputs, so it is fully unit-testable without a
6
+ database. The engine-specific parts (catalog queries, tracking-table SQL, apply
7
+ path) live in each tool's `schema.py`; this module is the common machinery.
8
+
9
+ Mirror layout (one file per object):
10
+ <out>/schema/<Type>/<schema>.<name>.sql (or <name>.sql when schema is "")
11
+ <out>/_manifest.json {relpath: sha256}
12
+
13
+ A "record" is a dict: {"type": <dir label>, "schema": <str|"">, "name": <str>,
14
+ "definition": <sql text>}.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import difflib
19
+ import hashlib
20
+ import json
21
+ import re
22
+ from pathlib import Path
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # hashing
26
+ # ---------------------------------------------------------------------------
27
+
28
+ def sha256_text(text: str) -> str:
29
+ return hashlib.sha256(text.encode("utf-8")).hexdigest()
30
+
31
+
32
+ def sha256_file(path: str | Path) -> str:
33
+ return hashlib.sha256(Path(path).read_bytes()).hexdigest()
34
+
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # mirror layout
38
+ # ---------------------------------------------------------------------------
39
+
40
+ _UNSAFE = re.compile(r'[<>:"/\\|?*\x00-\x1f]')
41
+
42
+
43
+ def _safe(component: str) -> str:
44
+ """Make one path component filesystem-safe (Windows + POSIX)."""
45
+ s = _UNSAFE.sub("_", str(component)).strip().rstrip(".")
46
+ return s or "_"
47
+
48
+
49
+ def mirror_relpath(obj_type: str, schema: str, name: str) -> str:
50
+ """Deterministic POSIX relpath under the mirror root for one object."""
51
+ stem = f"{schema}.{name}" if schema else str(name)
52
+ return f"schema/{_safe(obj_type)}/{_safe(stem)}.sql"
53
+
54
+
55
+ def write_mirror(out_dir: str | Path, records, *, full: bool = False,
56
+ dry_run: bool = False) -> dict:
57
+ """Write/update the local mirror from `records`. Incremental by default:
58
+ only changed/new objects are written, orphaned files are removed, and a
59
+ `_manifest.json` of relpath->sha256 tracks state. Returns a summary dict.
60
+ """
61
+ out = Path(out_dir)
62
+ schema_root = out / "schema"
63
+ manifest_path = out / "_manifest.json"
64
+
65
+ desired: dict[str, str] = {} # relpath -> definition text
66
+ by_type: dict[str, int] = {}
67
+ for r in records:
68
+ rel = mirror_relpath(r["type"], r.get("schema", "") or "", r["name"])
69
+ desired[rel] = r["definition"]
70
+ by_type[r["type"]] = by_type.get(r["type"], 0) + 1
71
+
72
+ prev: dict[str, str] = {}
73
+ if manifest_path.exists() and not full:
74
+ try:
75
+ prev = json.loads(manifest_path.read_text(encoding="utf-8"))
76
+ except (json.JSONDecodeError, OSError):
77
+ prev = {}
78
+
79
+ if full and not dry_run and schema_root.exists():
80
+ import shutil
81
+ shutil.rmtree(schema_root)
82
+ prev = {}
83
+
84
+ written = unchanged = removed = 0
85
+ new_manifest: dict[str, str] = {}
86
+ for rel, definition in sorted(desired.items()):
87
+ digest = sha256_text(definition)
88
+ new_manifest[rel] = digest
89
+ target = out / rel
90
+ is_same = prev.get(rel) == digest and (dry_run or target.exists())
91
+ if is_same and not full:
92
+ unchanged += 1
93
+ continue
94
+ written += 1
95
+ if not dry_run:
96
+ target.parent.mkdir(parents=True, exist_ok=True)
97
+ target.write_text(definition, encoding="utf-8")
98
+
99
+ # remove orphans (in the old manifest but no longer desired)
100
+ for rel in sorted(set(prev) - set(desired)):
101
+ removed += 1
102
+ if not dry_run:
103
+ try:
104
+ (out / rel).unlink()
105
+ except OSError:
106
+ pass
107
+
108
+ if not dry_run:
109
+ out.mkdir(parents=True, exist_ok=True)
110
+ manifest_path.write_text(
111
+ json.dumps(new_manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
112
+
113
+ return {"out": str(out), "written": written, "unchanged": unchanged,
114
+ "removed": removed, "total": len(desired), "by_type": by_type,
115
+ "dry_run": dry_run, "manifest": str(manifest_path)}
116
+
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # diff
120
+ # ---------------------------------------------------------------------------
121
+
122
+ def _mirror_files(mirror_dir: str | Path) -> dict[str, str]:
123
+ """relpath (posix, 'schema/...') -> file text, for every .sql under schema/."""
124
+ root = Path(mirror_dir) / "schema"
125
+ out: dict[str, str] = {}
126
+ if not root.is_dir():
127
+ return out
128
+ for p in root.rglob("*.sql"):
129
+ rel = p.relative_to(Path(mirror_dir)).as_posix()
130
+ try:
131
+ out[rel] = p.read_text(encoding="utf-8")
132
+ except OSError:
133
+ out[rel] = ""
134
+ return out
135
+
136
+
137
+ def diff_mirrors(left_dir: str | Path, right_dir: str | Path, *,
138
+ include_diff: bool = True) -> dict:
139
+ """Compare two schema mirrors. added = only in right, removed = only in left,
140
+ changed = present in both with differing content (with a unified diff)."""
141
+ left = _mirror_files(left_dir)
142
+ right = _mirror_files(right_dir)
143
+ added = sorted(set(right) - set(left))
144
+ removed = sorted(set(left) - set(right))
145
+ changed = []
146
+ for rel in sorted(set(left) & set(right)):
147
+ if left[rel] != right[rel]:
148
+ entry = {"relpath": rel}
149
+ if include_diff:
150
+ entry["diff"] = "".join(difflib.unified_diff(
151
+ left[rel].splitlines(keepends=True),
152
+ right[rel].splitlines(keepends=True),
153
+ fromfile=f"left/{rel}", tofile=f"right/{rel}"))
154
+ changed.append(entry)
155
+ unchanged = len(set(left) & set(right)) - len(changed)
156
+ return {"added": added, "removed": removed, "changed": changed,
157
+ "unchanged": unchanged,
158
+ "identical": not added and not removed and not changed,
159
+ "summary": {"added": len(added), "removed": len(removed),
160
+ "changed": len(changed)}}
161
+
162
+
163
+ # ---------------------------------------------------------------------------
164
+ # migrations
165
+ # ---------------------------------------------------------------------------
166
+
167
+ MIGRATION_FILE_RE = re.compile(r"^(\d{3,})_([a-z0-9][a-z0-9_\-]*)\.sql$", re.IGNORECASE)
168
+
169
+ _DANGER_RE = re.compile(
170
+ r"\b(DROP\s+(TABLE|DATABASE|SCHEMA|COLUMN)|TRUNCATE\s+TABLE)\b", re.IGNORECASE)
171
+ _UNQUALIFIED_RE = re.compile(
172
+ r"\b(UPDATE|DELETE)\b(?:(?!\bWHERE\b).)*?(;|\Z)", re.IGNORECASE | re.DOTALL)
173
+ _GO_RE = re.compile(r"^\s*GO\s*$", re.IGNORECASE)
174
+
175
+
176
+ def discover_migrations(migrations_dir: str | Path):
177
+ """Return [(mig_id, Path)] for NNNN_<slug>.sql files, ordered by the numeric
178
+ prefix. mig_id is the filename stem. Non-matching files are skipped (returned
179
+ separately so the caller can warn)."""
180
+ d = Path(migrations_dir)
181
+ found, skipped = [], []
182
+ if d.is_dir():
183
+ for p in sorted(d.iterdir()):
184
+ if p.is_file() and p.suffix.lower() == ".sql":
185
+ m = MIGRATION_FILE_RE.match(p.name)
186
+ (found if m else skipped).append((p.stem if m else p.name, p))
187
+ found.sort(key=lambda t: int(t[0].split("_", 1)[0]))
188
+ return found, [name for name, _ in skipped]
189
+
190
+
191
+ def pending(applied_ids, migrations):
192
+ """migrations = [(mig_id, path)]; return those whose id is not in applied_ids."""
193
+ applied = set(applied_ids)
194
+ return [(mid, p) for (mid, p) in migrations if mid not in applied]
195
+
196
+
197
+ def danger_warnings(sql: str) -> list[str]:
198
+ out = []
199
+ m = _DANGER_RE.search(sql)
200
+ if m:
201
+ out.append(f"destructive DDL: {m.group(0)}")
202
+ if _UNQUALIFIED_RE.search(sql):
203
+ out.append("possible UPDATE/DELETE without a WHERE clause")
204
+ return out
205
+
206
+
207
+ def split_go_batches(sql: str) -> list[str]:
208
+ """Split a SQL Server script on lines that are exactly `GO` (case-insensitive).
209
+ Known limitation: a `GO` inside a string literal or block comment that sits
210
+ alone on a line is still treated as a separator."""
211
+ # Lines keep their own endings: SQL Server stores a CREATE/ALTER of a module verbatim, so
212
+ # rejoining with "\n" turned every CRLF-normalized migration into an LF-only definition.
213
+ batches, cur = [], []
214
+ for line in sql.splitlines(keepends=True):
215
+ if _GO_RE.match(line.rstrip("\r\n")):
216
+ batch = "".join(cur).strip()
217
+ if batch:
218
+ batches.append(batch)
219
+ cur = []
220
+ else:
221
+ cur.append(line)
222
+ batch = "".join(cur).strip()
223
+ if batch:
224
+ batches.append(batch)
225
+ return batches