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,839 @@
1
+ """Browser driver abstraction for the web tool.
2
+
3
+ The whole point of this layer is testability: the declarative step interpreter
4
+ (``steps.py``) and the high-level API (``api.py``) talk only to the small
5
+ ``BrowserDriver`` interface below. Tests inject a ``FakeDriver`` (see
6
+ ``tests/conftest.py``) and run the entire tool with ZERO real browser.
7
+
8
+ ``PlaywrightDriver`` is the production implementation. Playwright is imported
9
+ **lazily inside methods** so importing this module (and running the hermetic
10
+ test suite) never requires the browser binaries to be installed.
11
+
12
+ Lifecycle
13
+ ---------
14
+ A driver wraps one Playwright *browser context* (optionally seeded from a saved
15
+ ``storageState``) and one *page*. Use it as a context manager::
16
+
17
+ with PlaywrightDriver(storage_state=state, headless=True) as d:
18
+ d.goto("https://example.com")
19
+ text = d.extract_text("h1")
20
+
21
+ ``__exit__`` always closes the page, context, browser, and Playwright runtime.
22
+ """
23
+ from __future__ import annotations
24
+
25
+ from typing import Any, Protocol, runtime_checkable
26
+
27
+ from tools.web.errors import BrowserError
28
+
29
+ DEFAULT_TIMEOUT_MS = 15000
30
+ DEFAULT_NAV_TIMEOUT_MS = 30000
31
+
32
+ # Chromium flag that drops the most common automation fingerprint. Applied to
33
+ # every launch (harmless when already headed). Paired with the UA cleanup in
34
+ # ``_apply_stealth`` so consumer web apps (notably WhatsApp Web, which gates on
35
+ # a "HeadlessChrome" UA) load normally instead of the "update Chrome" page.
36
+ _STEALTH_ARGS = ("--disable-blink-features=AutomationControlled",)
37
+
38
+
39
+ @runtime_checkable
40
+ class BrowserDriver(Protocol):
41
+ """The surface every action/step relies on. Implemented for real by
42
+ ``PlaywrightDriver`` and faked in tests. Keep this minimal and stable -
43
+ other tools import against this contract."""
44
+
45
+ def goto(self, url: str, *, timeout: int | None = None) -> None: ...
46
+
47
+ def click(self, selector: str, *, timeout: int | None = None,
48
+ force: bool = False) -> None: ...
49
+
50
+ def hover(self, selector: str, *, timeout: int | None = None) -> None: ...
51
+
52
+ def context_click(self, selector: str, *, timeout: int | None = None) -> None: ...
53
+
54
+ def mouse_click(self, x: float, y: float, *, button: str = "left",
55
+ move_first: bool = True) -> None: ...
56
+
57
+ def capture_download(self, resolve_path, trigger,
58
+ *, timeout: int | None = None) -> dict: ...
59
+
60
+ def fill(self, selector: str, text: str, *, timeout: int | None = None) -> None: ...
61
+
62
+ def press(self, key: str, *, selector: str | None = None) -> None: ...
63
+
64
+ def type_text(self, text: str, *, selector: str | None = None,
65
+ timeout: int | None = None) -> None: ...
66
+
67
+ def wait(self, ms: int) -> None: ...
68
+
69
+ def wait_for(self, selector: str, *, timeout: int | None = None) -> None: ...
70
+
71
+ def extract_text(self, selector: str | None = None,
72
+ *, timeout: int | None = None) -> str: ...
73
+
74
+ def extract_attr(self, selector: str, attr: str,
75
+ *, timeout: int | None = None) -> str | None: ...
76
+
77
+ def inner_html(self, selector: str, *, timeout: int | None = None) -> str: ...
78
+
79
+ def eval_js(self, script: str, *, arg: Any = None) -> Any: ...
80
+
81
+ def set_input_files(self, selector: str, files: Any,
82
+ *, timeout: int | None = None) -> None: ...
83
+
84
+ def set_files_via_chooser(self, trigger_selector: str, files: Any,
85
+ *, timeout: int | None = None) -> None: ...
86
+
87
+ def set_viewport(self, width: int, height: int) -> None: ...
88
+
89
+ def screenshot(self, path: str, *, full_page: bool = True) -> str: ...
90
+
91
+ def render_pdf(self, html: str, path: str, *, fmt: str = "A4",
92
+ width: str | None = None, height: str | None = None,
93
+ margin: str | None = None) -> str: ...
94
+
95
+ def current_url(self) -> str: ...
96
+
97
+ def get_cookies(self) -> list: ...
98
+
99
+ def add_cookies(self, cookies: list) -> None: ...
100
+
101
+
102
+ _PERSISTENT_STORAGE_JS = r"""
103
+ (() => {
104
+ try {
105
+ const sm = navigator.storage;
106
+ if (sm) {
107
+ sm.persist = () => Promise.resolve(true);
108
+ sm.persisted = () => Promise.resolve(true);
109
+ }
110
+ const sb = navigator.storageBuckets;
111
+ if (sb && sb.open) {
112
+ const open = sb.open.bind(sb);
113
+ sb.open = async (name, opts) => {
114
+ const bucket = await open(name, Object.assign({}, opts || {}, { persisted: false }));
115
+ try {
116
+ bucket.persist = () => Promise.resolve(true);
117
+ bucket.persisted = () => Promise.resolve(true);
118
+ } catch (e) {}
119
+ return bucket;
120
+ };
121
+ }
122
+ } catch (e) {}
123
+ })();
124
+ """
125
+
126
+
127
+ class PlaywrightDriver:
128
+ """Production ``BrowserDriver`` backed by Playwright's sync API.
129
+
130
+ Parameters
131
+ ----------
132
+ storage_state:
133
+ A Playwright storageState dict (from a saved session) or None for an
134
+ anonymous context.
135
+ headless:
136
+ Run without a visible window. Defaults True. ``save-session`` passes
137
+ False so the human can log in.
138
+ channel:
139
+ Optional Chromium channel ("chrome", "msedge"). When None, the bundled
140
+ Chromium build is used.
141
+ default_timeout:
142
+ Per-action default timeout in ms (selectors, fills, clicks).
143
+ """
144
+
145
+ def __init__(self, *, storage_state: dict | None = None, headless: bool = True,
146
+ channel: str | None = None, user_data_dir: str | None = None,
147
+ default_timeout: int = DEFAULT_TIMEOUT_MS,
148
+ nav_timeout: int = DEFAULT_NAV_TIMEOUT_MS,
149
+ extra_args: list | None = None):
150
+ self._storage_state = storage_state
151
+ # Extra Chromium launch flags (e.g. fake-mic for voice notes).
152
+ self._extra_args = list(extra_args or [])
153
+ self._headless = headless
154
+ self._channel = channel
155
+ # When set, use a PERSISTENT context (Chromium user-data-dir) instead of
156
+ # launch()+new_context(storage_state). Required for IndexedDB-auth sites
157
+ # like WhatsApp Web. Mutually exclusive with storage_state.
158
+ self._user_data_dir = user_data_dir
159
+ self._default_timeout = default_timeout
160
+ self._nav_timeout = nav_timeout
161
+ self._pw = None
162
+ self._browser = None
163
+ self._context = None
164
+ self._page = None
165
+ # runtime diagnostics collected during a session: console errors/warnings,
166
+ # uncaught page errors, failed requests, and HTTP error responses (>=400).
167
+ # This is the observability that lets verification SEE runtime failures
168
+ # (a 400 launch, a JS exception) instead of only guessing from a screenshot.
169
+ self._diag = {"console": [], "page_errors": [], "failed_requests": [], "bad_responses": []}
170
+
171
+ # --- lifecycle --------------------------------------------------------
172
+ def start(self) -> "PlaywrightDriver":
173
+ try:
174
+ from playwright.sync_api import sync_playwright
175
+ except ImportError as exc: # pragma: no cover - env without playwright
176
+ raise BrowserError(
177
+ "playwright is not installed. Run: uv add playwright"
178
+ ) from exc
179
+ try:
180
+ self._pw = sync_playwright().start()
181
+ if self._user_data_dir is not None:
182
+ # Persistent profile: one call returns a context (no separate
183
+ # browser object). The native on-disk profile carries the login.
184
+ ctx_kwargs: dict[str, Any] = {"headless": self._headless,
185
+ "args": list(_STEALTH_ARGS) + self._extra_args}
186
+ if self._channel:
187
+ ctx_kwargs["channel"] = self._channel
188
+ self._context = self._pw.chromium.launch_persistent_context(
189
+ self._user_data_dir, **ctx_kwargs)
190
+ self._browser = None
191
+ else:
192
+ launch_kwargs: dict[str, Any] = {"headless": self._headless,
193
+ "args": list(_STEALTH_ARGS) + self._extra_args}
194
+ if self._channel:
195
+ launch_kwargs["channel"] = self._channel
196
+ self._browser = self._pw.chromium.launch(**launch_kwargs)
197
+ ctx_kwargs = {}
198
+ if self._storage_state is not None:
199
+ ctx_kwargs["storage_state"] = self._storage_state
200
+ self._context = self._browser.new_context(**ctx_kwargs)
201
+ self._context.set_default_timeout(self._default_timeout)
202
+ self._context.set_default_navigation_timeout(self._nav_timeout)
203
+ # A persistent context opens with one page already; reuse it.
204
+ pages = self._context.pages
205
+ self._page = pages[0] if pages else self._context.new_page()
206
+ self._attach_diagnostics()
207
+ # Strip the headless/automation fingerprint so consumer web apps
208
+ # load (WhatsApp Web rejects the "HeadlessChrome" UA). Best-effort.
209
+ self._apply_stealth()
210
+ except Exception as exc: # noqa: BLE001
211
+ self.close()
212
+ raise _wrap(exc)
213
+ return self
214
+
215
+ def _attach_diagnostics(self) -> None:
216
+ """Listen for console errors/warnings, uncaught page errors, failed requests
217
+ and HTTP >=400 responses. Best-effort; capping each list keeps memory bounded."""
218
+ d = self._diag
219
+
220
+ def _cap(key, item):
221
+ if len(d[key]) < 100:
222
+ d[key].append(item)
223
+
224
+ def _console(msg):
225
+ try:
226
+ if msg.type in ("error", "warning"):
227
+ _cap("console", {"type": msg.type, "text": (msg.text or "")[:500]})
228
+ except Exception: # noqa: BLE001
229
+ pass
230
+
231
+ def _pageerror(err):
232
+ try:
233
+ _cap("page_errors", str(err)[:500])
234
+ except Exception: # noqa: BLE001
235
+ pass
236
+
237
+ def _reqfailed(req):
238
+ try:
239
+ _cap("failed_requests", {"url": (req.url or "")[:200], "method": req.method,
240
+ "failure": str(getattr(req, "failure", "") or "")[:200]})
241
+ except Exception: # noqa: BLE001
242
+ pass
243
+
244
+ def _response(resp):
245
+ try:
246
+ if resp.status >= 400:
247
+ _cap("bad_responses", {"url": (resp.url or "")[:200], "status": resp.status})
248
+ except Exception: # noqa: BLE001
249
+ pass
250
+ try:
251
+ self._page.on("console", _console)
252
+ self._page.on("pageerror", _pageerror)
253
+ self._page.on("requestfailed", _reqfailed)
254
+ self._page.on("response", _response)
255
+ except Exception: # noqa: BLE001 - diagnostics must never break a launch
256
+ pass
257
+
258
+ def diagnostics(self) -> dict:
259
+ """Snapshot of runtime issues seen so far: console errors/warnings, uncaught
260
+ page errors, failed requests, HTTP >=400 responses. Empty lists = clean run."""
261
+ return {k: list(v) for k, v in self._diag.items()}
262
+
263
+ def _apply_stealth(self) -> None:
264
+ """Make automated Chromium pass as a normal browser.
265
+
266
+ Two fingerprints break consumer web apps under automation: the
267
+ User-Agent (default headless Chromium reports "HeadlessChrome", which
268
+ WhatsApp Web's browser-support gate rejects, so it never reaches the
269
+ chat list) and ``navigator.webdriver``. We drop "Headless" from the UA
270
+ via CDP's ``Network.setUserAgentOverride`` (dynamic — no hard-coded
271
+ version) and shadow ``navigator.webdriver`` for future navigations.
272
+ Every step is guarded: stealth must never break a launch."""
273
+ try:
274
+ self._context.add_init_script(
275
+ "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"
276
+ )
277
+ except Exception: # noqa: BLE001 - best-effort hardening
278
+ pass
279
+ # Persistent storage. Automated Chromium answers navigator.storage.persist() and a
280
+ # persisted Storage Bucket with "denied", and WhatsApp Web (since 2026-10) halts on
281
+ # its loading splash with "[storage] storage bucket persistence denied
282
+ # (aquire-persistent-storage-denied)". Grant the real permission over CDP, and make
283
+ # the page-side checks agree for engines where the grant is not honoured.
284
+ try:
285
+ cdp = self._context.new_cdp_session(self._page)
286
+ cdp.send("Browser.grantPermissions", {"permissions": ["durableStorage"]})
287
+ except Exception: # noqa: BLE001 - best-effort hardening
288
+ pass
289
+ try:
290
+ self._context.add_init_script(_PERSISTENT_STORAGE_JS)
291
+ except Exception: # noqa: BLE001 - best-effort hardening
292
+ pass
293
+ try:
294
+ ua = self._page.evaluate("navigator.userAgent")
295
+ if ua and "Headless" in ua:
296
+ clean = ua.replace("HeadlessChrome", "Chrome")
297
+ client = self._context.new_cdp_session(self._page)
298
+ client.send("Network.setUserAgentOverride", {"userAgent": clean})
299
+ except Exception: # noqa: BLE001 - best-effort hardening
300
+ pass
301
+
302
+ def __enter__(self) -> "PlaywrightDriver":
303
+ return self.start()
304
+
305
+ def __exit__(self, *_exc) -> None:
306
+ self.close()
307
+
308
+ def close(self) -> None:
309
+ for obj, meth in (
310
+ (self._context, "close"),
311
+ (self._browser, "close"),
312
+ (self._pw, "stop"),
313
+ ):
314
+ try:
315
+ if obj is not None:
316
+ getattr(obj, meth)()
317
+ except Exception: # noqa: BLE001 - best-effort teardown
318
+ pass
319
+ self._page = self._context = self._browser = self._pw = None
320
+
321
+ # --- helpers ----------------------------------------------------------
322
+ @property
323
+ def page(self):
324
+ if self._page is None:
325
+ raise BrowserError("driver not started; call start() or use as a context manager")
326
+ return self._page
327
+
328
+ def storage_state(self, *, indexed_db: bool = True) -> dict:
329
+ """Capture the current context's cookies + localStorage (+ IndexedDB).
330
+
331
+ IndexedDB capture (Playwright >=1.51) is ON by default: sites like
332
+ WhatsApp Web keep their auth/session in IndexedDB, so a cookies+
333
+ localStorage-only snapshot does NOT restore the login. Restoration is
334
+ automatic — `new_context(storage_state=...)` replays IndexedDB too."""
335
+ if self._context is None:
336
+ raise BrowserError("driver not started")
337
+ try:
338
+ return self._context.storage_state(indexed_db=indexed_db)
339
+ except TypeError:
340
+ # Older Playwright without the indexed_db kwarg — degrade gracefully.
341
+ return self._context.storage_state()
342
+
343
+ # --- BrowserDriver surface -------------------------------------------
344
+ def goto(self, url: str, *, timeout: int | None = None) -> None:
345
+ try:
346
+ self.page.goto(url, timeout=timeout or self._nav_timeout)
347
+ except Exception as exc: # noqa: BLE001
348
+ raise _wrap(exc, {"url": url})
349
+
350
+ def click(self, selector: str, *, timeout: int | None = None,
351
+ force: bool = False) -> None:
352
+ """``force`` skips the actionability wait. Some frameworks paint a
353
+ container over their own controls, so the hit test at the element's
354
+ centre returns that container and the click is refused even though a
355
+ person can press the control. Use it only when that is what is
356
+ happening: it also skips the visibility check, so a genuinely hidden
357
+ element will be "clicked" with no effect and no error."""
358
+ try:
359
+ self.page.click(selector, timeout=timeout or self._default_timeout,
360
+ force=force)
361
+ except Exception as exc: # noqa: BLE001
362
+ raise _wrap(exc, {"selector": selector, "force": force})
363
+
364
+ def hover(self, selector: str, *, timeout: int | None = None) -> None:
365
+ """Move the mouse over the element - for UIs that reveal controls on
366
+ hover (e.g. WhatsApp Web's per-message context-menu chevron)."""
367
+ try:
368
+ self.page.hover(selector, timeout=timeout or self._default_timeout)
369
+ except Exception as exc: # noqa: BLE001
370
+ raise _wrap(exc, {"selector": selector})
371
+
372
+ def context_click(self, selector: str, *, timeout: int | None = None) -> None:
373
+ """Right-click the element - for app-rendered context menus (e.g.
374
+ WhatsApp Web's per-message menu, which opens on right-click)."""
375
+ try:
376
+ self.page.click(selector, button="right",
377
+ timeout=timeout or self._default_timeout)
378
+ except Exception as exc: # noqa: BLE001
379
+ raise _wrap(exc, {"selector": selector})
380
+
381
+ def fill(self, selector: str, text: str, *, timeout: int | None = None) -> None:
382
+ try:
383
+ self.page.fill(selector, text, timeout=timeout or self._default_timeout)
384
+ except Exception as exc: # noqa: BLE001
385
+ raise _wrap(exc, {"selector": selector})
386
+
387
+ def press(self, key: str, *, selector: str | None = None) -> None:
388
+ try:
389
+ if selector:
390
+ self.page.press(selector, key)
391
+ else:
392
+ self.page.keyboard.press(key)
393
+ except Exception as exc: # noqa: BLE001
394
+ raise _wrap(exc, {"key": key, "selector": selector})
395
+
396
+ def type_text(self, text: str, *, selector: str | None = None,
397
+ timeout: int | None = None) -> None:
398
+ """Type ``text`` via real keyboard events into the focused element.
399
+
400
+ Unlike ``fill`` (which sets the value of a DOM <input>/<textarea> by
401
+ selector), this drives ``keyboard.type`` so the keystrokes reach targets
402
+ that are NOT addressable in the DOM - e.g. a <canvas>-rendered grid like
403
+ Google Sheets, where you first jump to a cell via the Name Box and then
404
+ type into the now-focused canvas. If ``selector`` is given it is clicked
405
+ first to take focus; otherwise typing goes to whatever is focused."""
406
+ try:
407
+ if selector:
408
+ self.page.click(selector, timeout=timeout or self._default_timeout)
409
+ self.page.keyboard.type(text)
410
+ except Exception as exc: # noqa: BLE001
411
+ raise _wrap(exc, {"selector": selector})
412
+
413
+ def wait(self, ms: int) -> None:
414
+ """Sleep ``ms`` milliseconds (fixed delay).
415
+
416
+ For settling after an async action with no selector to await - e.g.
417
+ letting a debounced autosave flush before reading back. Prefer
418
+ ``wait_for`` on a selector when one exists; this is the blunt fallback."""
419
+ try:
420
+ self.page.wait_for_timeout(ms)
421
+ except Exception as exc: # noqa: BLE001
422
+ raise _wrap(exc, {"ms": ms})
423
+
424
+ def wait_for(self, selector: str, *, timeout: int | None = None) -> None:
425
+ try:
426
+ self.page.wait_for_selector(selector, timeout=timeout or self._default_timeout)
427
+ except Exception as exc: # noqa: BLE001
428
+ raise _wrap(exc, {"selector": selector})
429
+
430
+ # --- frame-aware helpers ---------------------------------------------
431
+ # Some SPA overlays (e.g. LinkedIn's message compose bubble) render their
432
+ # input into a CHILD frame and/or seconds after the click, so a main-frame
433
+ # page.evaluate snapshot misses them. These poll the main frame AND every
434
+ # child frame with auto-waiting locators until the selector shows up.
435
+
436
+ def find_in_frames(self, selector: str, *, timeout: int | None = None,
437
+ step_ms: int = 400) -> int:
438
+ """Index of the first frame (0 = main) with ``selector`` VISIBLE, polling
439
+ until ``timeout``; -1 if none. Frame order matches ``self.page.frames``."""
440
+ t = timeout or self._default_timeout
441
+ waited = 0
442
+ while True:
443
+ for i, fr in enumerate(self.page.frames):
444
+ try:
445
+ loc = fr.locator(selector).first
446
+ if loc.count() > 0 and loc.is_visible():
447
+ return i
448
+ except Exception: # noqa: BLE001 - a frame may be detached mid-poll
449
+ continue
450
+ if waited >= t:
451
+ return -1
452
+ self.page.wait_for_timeout(step_ms)
453
+ waited += step_ms
454
+
455
+ def frame_click(self, frame_index: int, selector: str,
456
+ *, timeout: int | None = None) -> None:
457
+ t = timeout or self._default_timeout
458
+ loc = self.page.frames[frame_index].locator(selector).first
459
+ try:
460
+ try:
461
+ loc.click(timeout=t)
462
+ except Exception: # noqa: BLE001 - actionability check failed; scroll + force
463
+ try:
464
+ loc.scroll_into_view_if_needed(timeout=t)
465
+ except Exception: # noqa: BLE001
466
+ pass
467
+ loc.click(timeout=t, force=True)
468
+ except Exception as exc: # noqa: BLE001
469
+ raise _wrap(exc, {"frame": frame_index, "selector": selector})
470
+
471
+ def frame_type(self, frame_index: int, selector: str, text: str,
472
+ *, timeout: int | None = None) -> None:
473
+ """Focus ``selector`` in a frame then type via keyboard so the keystrokes
474
+ reach a contenteditable that ``fill`` can't set. Uses ``focus()`` (no
475
+ pointer-event actionability check, which the compose box in a child frame
476
+ can fail), falling back to a forced click."""
477
+ t = timeout or self._default_timeout
478
+ loc = self.page.frames[frame_index].locator(selector).first
479
+ try:
480
+ # locator.evaluate runs JS on the EXACT element Playwright matched -
481
+ # shadow-DOM aware AND with no viewport actionability check, so it works
482
+ # even when the compose box renders inside a web component / off-viewport
483
+ # (a plain document.querySelector can't see into an open shadow root).
484
+ try:
485
+ loc.evaluate("el => { try { el.scrollIntoView({block:'center'}); } catch(e){} el.focus(); }")
486
+ except Exception: # noqa: BLE001 - fall back to the actionable path
487
+ loc.focus(timeout=t)
488
+ self.page.keyboard.type(text)
489
+ except Exception as exc: # noqa: BLE001
490
+ raise _wrap(exc, {"frame": frame_index, "selector": selector})
491
+
492
+ def frame_el_click(self, frame_index: int, selector: str,
493
+ *, timeout: int | None = None) -> bool:
494
+ """Click the matched element via ``locator.evaluate`` - shadow-DOM aware and
495
+ with no viewport actionability check, for a Send button that renders
496
+ off-viewport or inside a web component. Returns True if clicked (not disabled)."""
497
+ try:
498
+ return bool(self.page.frames[frame_index].locator(selector).first.evaluate(
499
+ "el => { if (el.disabled) return false; el.click(); return true; }"))
500
+ except Exception: # noqa: BLE001
501
+ return False
502
+
503
+ def frame_count(self, frame_index: int, selector: str) -> int:
504
+ """Count elements matching ``selector`` in a frame via a Playwright
505
+ locator (shadow-DOM aware). Used to detect stale/extra compose bubbles."""
506
+ try:
507
+ return int(self.page.frames[frame_index].locator(selector).count())
508
+ except Exception: # noqa: BLE001
509
+ return 0
510
+
511
+ def _scoped(self, frame_index: int, container_sel: str, has_text: str, inner_sel: str):
512
+ """Locator for ``inner_sel`` inside the ``container_sel`` element that
513
+ CONTAINS ``has_text`` - e.g. the compose box inside the conversation bubble
514
+ addressed to a specific person. filter(has_text=...) is Unicode-safe and
515
+ shadow-DOM aware, so it targets the RIGHT box even when stale bubbles are open."""
516
+ fr = self.page.frames[frame_index]
517
+ return fr.locator(container_sel).filter(has_text=has_text).locator(inner_sel)
518
+
519
+ def frame_scoped_count(self, frame_index: int, container_sel: str, has_text: str,
520
+ inner_sel: str) -> int:
521
+ try:
522
+ return int(self._scoped(frame_index, container_sel, has_text, inner_sel).count())
523
+ except Exception: # noqa: BLE001
524
+ return 0
525
+
526
+ def frame_scoped_text(self, frame_index: int, container_sel: str, has_text: str,
527
+ inner_sel: str) -> str:
528
+ try:
529
+ return self._scoped(frame_index, container_sel, has_text, inner_sel).first.inner_text(
530
+ timeout=self._default_timeout) or ""
531
+ except Exception: # noqa: BLE001
532
+ return ""
533
+
534
+ def frame_scoped_type(self, frame_index: int, container_sel: str, has_text: str,
535
+ inner_sel: str, text: str, *, timeout: int | None = None) -> None:
536
+ loc = self._scoped(frame_index, container_sel, has_text, inner_sel).first
537
+ try:
538
+ try:
539
+ loc.evaluate("el => { try { el.scrollIntoView({block:'center'}); } catch(e){} el.focus(); }")
540
+ except Exception: # noqa: BLE001
541
+ loc.focus(timeout=timeout or self._default_timeout)
542
+ self.page.keyboard.type(text)
543
+ except Exception as exc: # noqa: BLE001
544
+ raise _wrap(exc, {"frame": frame_index, "container": container_sel, "has_text": has_text})
545
+
546
+ def frame_scoped_click(self, frame_index: int, container_sel: str, has_text: str,
547
+ inner_sel: str) -> bool:
548
+ try:
549
+ return bool(self._scoped(frame_index, container_sel, has_text, inner_sel).first.evaluate(
550
+ "el => { if (el.disabled) return false; el.click(); return true; }"))
551
+ except Exception: # noqa: BLE001
552
+ return False
553
+
554
+ def frame_bubble_recipients(self, frame_index: int, container_sel: str,
555
+ inner_sels: list) -> list:
556
+ """For EACH container matching ``container_sel``, the first non-empty
557
+ inner_text among ``inner_sels`` (tried in order). Returns a list aligned to
558
+ container index - lets the caller pick the bubble addressed to a person with
559
+ diacritic-insensitive matching in Python (Playwright has_text cannot)."""
560
+ try:
561
+ bubbles = self.page.frames[frame_index].locator(container_sel)
562
+ out = []
563
+ for i in range(bubbles.count()):
564
+ txt = ""
565
+ for sel in inner_sels:
566
+ loc = bubbles.nth(i).locator(sel)
567
+ if loc.count():
568
+ txt = loc.first.inner_text(timeout=self._default_timeout) or ""
569
+ if txt.strip():
570
+ break
571
+ out.append(txt)
572
+ return out
573
+ except Exception: # noqa: BLE001
574
+ return []
575
+
576
+ def frame_nth_text(self, frame_index: int, container_sel: str, index: int,
577
+ inner_sel: str) -> str:
578
+ try:
579
+ return self.page.frames[frame_index].locator(container_sel).nth(index).locator(
580
+ inner_sel).first.inner_text(timeout=self._default_timeout) or ""
581
+ except Exception: # noqa: BLE001
582
+ return ""
583
+
584
+ def frame_nth_type(self, frame_index: int, container_sel: str, index: int,
585
+ inner_sel: str, text: str, *, timeout: int | None = None) -> None:
586
+ loc = self.page.frames[frame_index].locator(container_sel).nth(index).locator(inner_sel).first
587
+ try:
588
+ try:
589
+ loc.evaluate("el => { try { el.scrollIntoView({block:'center'}); } catch(e){} el.focus(); }")
590
+ except Exception: # noqa: BLE001
591
+ loc.focus(timeout=timeout or self._default_timeout)
592
+ self.page.keyboard.type(text)
593
+ except Exception as exc: # noqa: BLE001
594
+ raise _wrap(exc, {"frame": frame_index, "container": container_sel, "index": index})
595
+
596
+ def frame_nth_click(self, frame_index: int, container_sel: str, index: int,
597
+ inner_sel: str) -> bool:
598
+ try:
599
+ return bool(self.page.frames[frame_index].locator(container_sel).nth(index).locator(
600
+ inner_sel).first.evaluate("el => { if (el.disabled) return false; el.click(); return true; }"))
601
+ except Exception: # noqa: BLE001
602
+ return False
603
+
604
+ def frame_text(self, frame_index: int, selector: str,
605
+ *, timeout: int | None = None) -> str:
606
+ try:
607
+ return self.page.frames[frame_index].locator(selector).first.inner_text(
608
+ timeout=timeout or self._default_timeout) or ""
609
+ except Exception: # noqa: BLE001 - best-effort read for the recipient gate
610
+ return ""
611
+
612
+ def frame_eval(self, frame_index: int, script: str, *, arg: Any = None) -> Any:
613
+ """Evaluate JS inside a specific frame. Used to click a button that renders
614
+ off-viewport (a JS ``.click()`` ignores Playwright's viewport actionability
615
+ check, and a real <button> fires its handler)."""
616
+ try:
617
+ return self.page.frames[frame_index].evaluate(script, arg)
618
+ except Exception as exc: # noqa: BLE001
619
+ raise _wrap(exc, {"frame": frame_index, "script": script[:120]})
620
+
621
+ def extract_text(self, selector: str | None = None,
622
+ *, timeout: int | None = None) -> str:
623
+ try:
624
+ if selector:
625
+ el = self.page.wait_for_selector(
626
+ selector, timeout=timeout or self._default_timeout
627
+ )
628
+ return (el.inner_text() if el else "") or ""
629
+ return self.page.inner_text("body") or ""
630
+ except Exception as exc: # noqa: BLE001
631
+ raise _wrap(exc, {"selector": selector})
632
+
633
+ def extract_attr(self, selector: str, attr: str,
634
+ *, timeout: int | None = None) -> str | None:
635
+ try:
636
+ el = self.page.wait_for_selector(
637
+ selector, timeout=timeout or self._default_timeout
638
+ )
639
+ return el.get_attribute(attr) if el else None
640
+ except Exception as exc: # noqa: BLE001
641
+ raise _wrap(exc, {"selector": selector, "attr": attr})
642
+
643
+ def inner_html(self, selector: str, *, timeout: int | None = None) -> str:
644
+ """Return the innerHTML of the first element matching ``selector``.
645
+
646
+ Useful for structured extraction (e.g. reading a rendered <table>'s rows
647
+ and cells) where the joined inner_text of ``extract_text`` loses the cell
648
+ boundaries. Waits for the element first.
649
+ """
650
+ try:
651
+ el = self.page.wait_for_selector(
652
+ selector, timeout=timeout or self._default_timeout
653
+ )
654
+ return (el.inner_html() if el else "") or ""
655
+ except Exception as exc: # noqa: BLE001
656
+ raise _wrap(exc, {"selector": selector})
657
+
658
+ def eval_js(self, script: str, *, arg: Any = None) -> Any:
659
+ """Evaluate a JS expression/function in the page and return its result.
660
+
661
+ ``script`` may be a JS expression or an arrow/function body; ``arg`` is
662
+ passed through to Playwright's ``page.evaluate``. Returns the JSON-able
663
+ result. Escape hatch for DOM reads not covered by the typed methods.
664
+ """
665
+ try:
666
+ return self.page.evaluate(script, arg)
667
+ except Exception as exc: # noqa: BLE001
668
+ raise _wrap(exc, {"script": script[:120]})
669
+
670
+ def set_input_files(self, selector: str, files: Any,
671
+ *, timeout: int | None = None) -> None:
672
+ """Set files on an ``<input type=file>`` (for uploads / media attach).
673
+
674
+ ``files`` is a path or list of paths. The input is often hidden, so we
675
+ set files directly rather than clicking - the standard Playwright way to
676
+ drive a file picker (e.g. WhatsApp Web's attach -> document/photo input)."""
677
+ try:
678
+ self.page.set_input_files(selector, files,
679
+ timeout=timeout or self._default_timeout)
680
+ except Exception as exc: # noqa: BLE001
681
+ raise _wrap(exc, {"selector": selector})
682
+
683
+ def set_files_via_chooser(self, trigger_selector: str, files: Any,
684
+ *, timeout: int | None = None) -> None:
685
+ """Attach files that live behind a NATIVE file chooser: click
686
+ ``trigger_selector`` (e.g. a WhatsApp 'Photos & videos' menu item)
687
+ WHILE expecting the chooser, then hand it ``files``. The only reliable
688
+ way to attach on sites whose real ``<input type=file>`` exists only
689
+ transiently for the chooser (WhatsApp Web) — set_input_files on a
690
+ selector can't see it."""
691
+ t = timeout or self._default_timeout
692
+ try:
693
+ with self.page.expect_file_chooser(timeout=t) as fc:
694
+ self.page.click(trigger_selector, timeout=t)
695
+ fc.value.set_files(files, timeout=t)
696
+ except Exception as exc: # noqa: BLE001
697
+ raise _wrap(exc, {"selector": trigger_selector})
698
+
699
+ def mouse_click(self, x: float, y: float, *, button: str = "left",
700
+ move_first: bool = True) -> None:
701
+ """Click at absolute viewport COORDINATES (not a selector).
702
+
703
+ For a target a CSS selector can't address cleanly - e.g. right-clicking
704
+ a WhatsApp Web media bubble to open its context menu, where the Download
705
+ control lives only in that menu. ``move_first`` mimics a human pointer
706
+ (move, settle, then click), which some SPA menus need to register."""
707
+ try:
708
+ if move_first:
709
+ self.page.mouse.move(x, y)
710
+ self.page.wait_for_timeout(120)
711
+ self.page.mouse.click(x, y, button=button)
712
+ except Exception as exc: # noqa: BLE001
713
+ raise _wrap(exc, {"x": x, "y": y, "button": button})
714
+
715
+ def capture_download(self, resolve_path, trigger,
716
+ *, timeout: int | None = None) -> dict:
717
+ """Capture a browser DOWNLOAD started by ``trigger`` and save it.
718
+
719
+ ``trigger`` is a zero-arg callable that performs the click which starts
720
+ the download (e.g. clicking a context-menu 'Download' item); it MUST run
721
+ inside Playwright's ``expect_download`` scope, because the listener is
722
+ armed before the click and resolves when the transfer begins. Once the
723
+ download object exists we know its ``suggested_filename``, so
724
+ ``resolve_path(suggested) -> final_path`` lets the caller decide the
725
+ destination (a directory + the WhatsApp filename, or an explicit path).
726
+ The parent dir is created; returns
727
+ ``{suggested_filename, saved_path, trigger_result}``. Downloads work
728
+ headless too - accept_downloads defaults True on launch() and
729
+ launch_persistent_context()."""
730
+ import os as _os
731
+ t = timeout or self._nav_timeout
732
+ try:
733
+ with self.page.expect_download(timeout=t) as dl:
734
+ trigger_result = trigger()
735
+ download = dl.value
736
+ suggested = download.suggested_filename
737
+ final = resolve_path(suggested)
738
+ parent = _os.path.dirname(_os.path.abspath(final))
739
+ if parent:
740
+ _os.makedirs(parent, exist_ok=True)
741
+ download.save_as(final)
742
+ return {"suggested_filename": suggested, "saved_path": str(final),
743
+ "trigger_result": trigger_result}
744
+ except Exception as exc: # noqa: BLE001
745
+ raise _wrap(exc, {"download": True})
746
+
747
+ def set_viewport(self, width: int, height: int) -> None:
748
+ """Resize the page viewport (e.g. 1920x1080 for video-grade captures).
749
+ The context default is 1280x720; this changes only the live page."""
750
+ try:
751
+ self.page.set_viewport_size({"width": int(width), "height": int(height)})
752
+ except Exception as exc: # noqa: BLE001
753
+ raise _wrap(exc, {"viewport": f"{width}x{height}"})
754
+
755
+ def screenshot(self, path: str, *, full_page: bool = True) -> str:
756
+ try:
757
+ self.page.screenshot(path=path, full_page=full_page)
758
+ return path
759
+ except Exception as exc: # noqa: BLE001
760
+ raise _wrap(exc, {"path": path})
761
+
762
+ def render_pdf(self, html: str, path: str, *, fmt: str = "A4",
763
+ width: str | None = None, height: str | None = None,
764
+ margin: str | None = None) -> str:
765
+ """Render an HTML string to a PDF file via Chromium's print engine.
766
+
767
+ ``page.pdf()`` is Chromium/headless-only (the driver's default); it lays
768
+ the HTML out with print CSS and writes ``path``. Used for deterministic
769
+ document generation (e.g. reconstructing an invoice) with no live site.
770
+
771
+ ``width`` + ``height`` (CSS lengths such as ``"1000mm"``) set a custom
772
+ page size and take precedence over ``fmt`` - large-format print (a
773
+ roll-up, a poster) needs an exact sheet, not a paper name. ``margin``
774
+ (one CSS length, all four sides; ``"0"`` for full-bleed artwork)
775
+ replaces the default document margins.
776
+ """
777
+ try:
778
+ self.page.set_content(html, wait_until="load")
779
+ # Web fonts may still be decoding after "load"; printing before they
780
+ # are ready silently falls back to a system font.
781
+ self.page.evaluate("document.fonts.ready.then(() => true)")
782
+ opts = {"path": path, "print_background": True}
783
+ if width and height:
784
+ opts.update(width=width, height=height)
785
+ else:
786
+ opts["format"] = fmt
787
+ if margin is None:
788
+ opts["margin"] = {"top": "12mm", "bottom": "14mm", "left": "12mm", "right": "12mm"}
789
+ else:
790
+ opts["margin"] = {"top": margin, "bottom": margin, "left": margin, "right": margin}
791
+ self.page.pdf(**opts)
792
+ return path
793
+ except Exception as exc: # noqa: BLE001
794
+ raise _wrap(exc, {"path": path})
795
+
796
+ def current_url(self) -> str:
797
+ return self.page.url
798
+
799
+ # --- cookies ----------------------------------------------------------
800
+ def get_cookies(self) -> list:
801
+ """Return ALL cookies in the current context (every domain).
802
+
803
+ Crucially this includes *session-scoped* cookies (no Expires/Max-Age)
804
+ while the browser is alive — exactly the ones Chromium will NOT write to
805
+ a persistent profile on disk. ``save_session`` snapshots these so they
806
+ can be re-injected on the next open. Returns [] if the context is gone."""
807
+ if self._context is None:
808
+ return []
809
+ try:
810
+ return list(self._context.cookies())
811
+ except Exception: # noqa: BLE001 - best-effort; never break a save
812
+ return []
813
+
814
+ def add_cookies(self, cookies: list) -> None:
815
+ """Inject cookies into the current context (before navigating).
816
+
817
+ Accepts the same dicts ``get_cookies`` returns (domain+path+value+...),
818
+ so a saved sidecar round-trips. Best-effort: a bad cookie list must not
819
+ prevent the profile from opening — we drop entries Playwright rejects."""
820
+ if self._context is None or not cookies:
821
+ return
822
+ try:
823
+ self._context.add_cookies(cookies)
824
+ except Exception: # noqa: BLE001
825
+ # Fall back to injecting valid entries one-by-one so one malformed
826
+ # cookie can't sink the whole (still-authenticating) batch.
827
+ for c in cookies:
828
+ try:
829
+ self._context.add_cookies([c])
830
+ except Exception: # noqa: BLE001
831
+ pass
832
+
833
+
834
+ def _wrap(exc: Exception, details: dict | None = None) -> BrowserError:
835
+ """Normalize any Playwright exception into a BrowserError with details."""
836
+ if isinstance(exc, BrowserError):
837
+ return exc
838
+ msg = str(exc).strip().splitlines()[0] if str(exc).strip() else exc.__class__.__name__
839
+ return BrowserError(msg, details or {})