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,544 @@
1
+ """Pull published contact details out of free text or an HTML page.
2
+
3
+ Shared by the tools that read what a person publishes about themselves (a YouTube
4
+ channel description, a video description, a link-in-bio page, a personal site):
5
+ the email they list for business inquiries, their profiles on other platforms, and
6
+ any affiliate-shaped links (which say "this person already earns commission").
7
+
8
+ Pure functions, no I/O. Callers attach their own ``source`` to what comes back.
9
+
10
+ What is deliberately NOT done here: guessing addresses (first.last@domain),
11
+ resolving shortened links, or following redirects. Only what is literally on the
12
+ page counts, so every contact can be traced back to where it was published.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import html as _html
17
+ import json
18
+ import re
19
+ import unicodedata
20
+ from html.parser import HTMLParser
21
+ from urllib.parse import parse_qsl, unquote, urljoin, urlsplit
22
+
23
+ # --- emails -------------------------------------------------------------------
24
+
25
+ EMAIL_RE = re.compile(
26
+ r"(?<![\w.+%-])([A-Za-z0-9][A-Za-z0-9._%+-]*@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,24})(?![\w-])"
27
+ )
28
+
29
+ # "name [at] domain [dot] com", "name (at) domain (dot) com", "name{at}domain{dot}com".
30
+ # A bare "name at domain dot com" is NOT matched: it is too common in normal prose.
31
+ _AT = r"\s*[\[\(\{<]\s*at\s*[\]\)\}>]\s*"
32
+ _DOT = r"\s*(?:[\[\(\{<]\s*dot\s*[\]\)\}>]|\.)\s*"
33
+ OBFUSCATED_EMAIL_RE = re.compile(
34
+ rf"([A-Za-z0-9][A-Za-z0-9._%+-]*){_AT}([A-Za-z0-9-]+(?:{_DOT}[A-Za-z0-9-]+)+)",
35
+ re.IGNORECASE,
36
+ )
37
+ _DOT_TOKEN_RE = re.compile(_DOT, re.IGNORECASE)
38
+
39
+ # File extensions that look like a TLD in retina asset names (an "@2x" suffix before ".png").
40
+ _NOT_TLDS = {"png", "jpg", "jpeg", "gif", "webp", "svg", "css", "js", "ico", "mp4", "pdf"}
41
+ # Mailboxes nobody reads: never a contact, wherever they appear.
42
+ _NO_REPLY = re.compile(r"^(no-?reply|do-?not-?reply|mailer-daemon|postmaster|bounces?)([+.-].*)?$")
43
+ # Personal mailbox providers: an address here belongs to a person, not to the site
44
+ # it was read from.
45
+ FREE_MAIL_DOMAINS = {
46
+ "gmail.com", "googlemail.com", "outlook.com", "hotmail.com", "live.com", "msn.com",
47
+ "yahoo.com", "icloud.com", "me.com", "proton.me", "protonmail.com", "aol.com",
48
+ "gmx.com", "gmx.de", "gmx.net", "yandex.com", "zoho.com", "mail.com",
49
+ }
50
+ # Placeholder / infrastructure domains that are never a person's inbox.
51
+ _JUNK_EMAIL_DOMAINS = {
52
+ "example.com", "example.org", "example.net", "domain.com", "yourdomain.com",
53
+ "email.example", "sentry.io", "wixpress.com", "sentry-next.wixpress.com",
54
+ }
55
+
56
+
57
+ def _clean_email(raw: str) -> str | None:
58
+ email = raw.strip().strip(".").lower()
59
+ if email.count("@") != 1:
60
+ return None
61
+ local, domain = email.split("@")
62
+ if not local or not domain or ".." in email:
63
+ return None
64
+ if domain.rsplit(".", 1)[-1] in _NOT_TLDS:
65
+ return None
66
+ if domain in _JUNK_EMAIL_DOMAINS or domain.endswith(".example"):
67
+ return None
68
+ if _NO_REPLY.match(local):
69
+ return None
70
+ return email
71
+
72
+
73
+ def drop_shadowed(emails: list[str]) -> list[str]:
74
+ """Drop an address that is a cut-off copy of another one in the same list: same
75
+ local part, and one domain a strict prefix of the other's (a truncated top-level
76
+ domain such as ``.ne`` beside the full ``.net``). Creators mistype or truncate their
77
+ address in one place and get it right everywhere else; the complete one is the one
78
+ that delivers."""
79
+ out = []
80
+ for e in emails:
81
+ local, dom = e.split("@", 1)
82
+ if any(f != e and f.split("@", 1)[0] == local and f.split("@", 1)[1].startswith(dom)
83
+ for f in emails):
84
+ continue
85
+ out.append(e)
86
+ return out
87
+
88
+
89
+ def email_fits_page(email: str, page_url: str) -> bool:
90
+ """Whether an address read off ``page_url`` plausibly belongs to that page's owner.
91
+
92
+ Any address on a link-in-bio page counts (the person put it there). On any other
93
+ page only an address on the page's own site, or a personal free-mail address, does:
94
+ a creator's video links to tools and vendors, and those pages carry the VENDOR's
95
+ support inbox, which is not a way to reach the creator.
96
+ """
97
+ if classify_url(page_url) == "link_in_bio":
98
+ return True
99
+ domain = email.rsplit("@", 1)[-1].lower()
100
+ if domain in FREE_MAIL_DOMAINS:
101
+ return True
102
+ return _site(domain) == _site(_host(page_url))
103
+
104
+
105
+ def find_emails(text: str) -> list[str]:
106
+ """Every distinct email in ``text``, in order of appearance (lowercased)."""
107
+ found: list[str] = []
108
+ for m in EMAIL_RE.finditer(text or ""):
109
+ e = _clean_email(m.group(1))
110
+ if e and e not in found:
111
+ found.append(e)
112
+ for m in OBFUSCATED_EMAIL_RE.finditer(text or ""):
113
+ domain = _DOT_TOKEN_RE.sub(".", m.group(2))
114
+ e = _clean_email(f"{m.group(1)}@{domain}")
115
+ if e and "." in domain and e not in found:
116
+ found.append(e)
117
+ return found
118
+
119
+
120
+ # --- links --------------------------------------------------------------------
121
+
122
+ URL_RE = re.compile(r"https?://[^\s<>\"'`\]\[|]+", re.IGNORECASE)
123
+ # Profile links people often write without a scheme.
124
+ BARE_PROFILE_RE = re.compile(
125
+ r"(?<![\w./-])((?:www\.)?(?:linkedin\.com/(?:in|company)/|x\.com/|twitter\.com/)[\w%./-]+)",
126
+ re.IGNORECASE,
127
+ )
128
+ _TRAILING = ".,;:!?)]}'\"*"
129
+
130
+ LINK_IN_BIO_HOSTS = {
131
+ "linktr.ee", "beacons.ai", "stan.store", "bio.link", "lnk.bio", "linkin.bio",
132
+ "taplink.cc", "campsite.bio", "solo.to", "hoo.be", "msha.ke", "linkpop.com",
133
+ "koji.to", "withkoji.com", "bento.me", "allmylinks.com", "linkr.bio", "contactin.bio",
134
+ }
135
+ _PLATFORM_HOSTS = {
136
+ "linkedin": ("linkedin.com", "lnkd.in"),
137
+ "x": ("x.com", "twitter.com"),
138
+ "youtube": ("youtube.com", "youtu.be"),
139
+ "instagram": ("instagram.com",),
140
+ "tiktok": ("tiktok.com",),
141
+ "facebook": ("facebook.com", "fb.com", "fb.me"),
142
+ "github": ("github.com",),
143
+ "booking": ("calendly.com", "cal.com", "tidycal.com", "savvycal.com", "zcal.co"),
144
+ "newsletter": ("substack.com", "beehiiv.com", "convertkit.com", "kit.com"),
145
+ }
146
+ # Shorteners and stores: a link, but neither the person's site nor a profile.
147
+ _OTHER_HOSTS = (
148
+ "bit.ly", "tinyurl.com", "geni.us", "amzn.to", "amazon.com", "t.co", "goo.gl",
149
+ "ow.ly", "rebrand.ly", "shorturl.at", "buff.ly", "discord.gg", "discord.com",
150
+ "patreon.com", "buymeacoffee.com", "ko-fi.com", "gumroad.com", "udemy.com",
151
+ "apple.com", "spotify.com", "google.com", "goo.gl", "forms.gle",
152
+ )
153
+ LINK_KINDS = ("linkedin", "x", "youtube", "instagram", "tiktok", "facebook", "github",
154
+ "booking", "newsletter", "link_in_bio", "website", "other")
155
+
156
+
157
+ def _host(url: str) -> str:
158
+ try:
159
+ host = (urlsplit(url).hostname or "").lower()
160
+ except ValueError:
161
+ return ""
162
+ return host[4:] if host.startswith("www.") else host
163
+
164
+
165
+ def _host_matches(host: str, domains) -> bool:
166
+ return any(host == d or host.endswith("." + d) for d in domains)
167
+
168
+
169
+ def normalize_url(raw: str) -> str:
170
+ url = raw.strip().rstrip(_TRAILING)
171
+ if not re.match(r"https?://", url, re.IGNORECASE):
172
+ url = "https://" + url
173
+ return url
174
+
175
+
176
+ # Static files: never a profile, a site or a contact route.
177
+ _ASSET_RE = re.compile(
178
+ r"\.(?:js|mjs|css|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|json|webmanifest|map|mp4|webm)$",
179
+ re.IGNORECASE)
180
+
181
+
182
+ def is_asset(url: str) -> bool:
183
+ try:
184
+ return bool(_ASSET_RE.search(urlsplit(url).path))
185
+ except ValueError:
186
+ return False
187
+
188
+
189
+ def find_urls(text: str) -> list[str]:
190
+ """Distinct http(s) URLs plus scheme-less LinkedIn / X profile links.
191
+ Static assets (scripts, styles, images, fonts) are skipped."""
192
+ text = (text or "").replace("\\/", "/")
193
+ out: list[str] = []
194
+ for m in URL_RE.finditer(text):
195
+ u = normalize_url(m.group(0))
196
+ if _host(u) and not is_asset(u) and u not in out:
197
+ out.append(u)
198
+ for m in BARE_PROFILE_RE.finditer(text):
199
+ u = normalize_url(m.group(1))
200
+ if u not in out:
201
+ out.append(u)
202
+ return out
203
+
204
+
205
+ def classify_url(url: str) -> str:
206
+ """Which kind of link this is (one of LINK_KINDS)."""
207
+ host = _host(url)
208
+ if not host:
209
+ return "other"
210
+ if _host_matches(host, LINK_IN_BIO_HOSTS) or host.endswith(".carrd.co"):
211
+ return "link_in_bio"
212
+ for kind, domains in _PLATFORM_HOSTS.items():
213
+ if _host_matches(host, domains):
214
+ return kind
215
+ if _host_matches(host, _OTHER_HOSTS):
216
+ return "other"
217
+ return "website"
218
+
219
+
220
+ # --- affiliate-shaped links ---------------------------------------------------
221
+
222
+ # Query parameters affiliate / referral programs put on a link, and the network
223
+ # that uses each (None = generic referral parameter used by many programs).
224
+ AFFILIATE_PARAMS = {
225
+ "via": "rewardful",
226
+ "fpr": "firstpromoter",
227
+ "fp_ref": "firstpromoter",
228
+ "tap_a": "tapfiliate",
229
+ "irclickid": "impact",
230
+ "aff": None, "affiliate": None, "aff_id": None, "affid": None, "affiliate_id": None,
231
+ "ref": None, "referral": None, "referral_code": None, "ref_id": None,
232
+ "pc": None, "partner": None, "partner_id": None, "promo": None,
233
+ "tag": "amazon",
234
+ }
235
+ # Hosts that only exist to track affiliate clicks.
236
+ AFFILIATE_HOSTS = {
237
+ "partnerlinks.io": "partnerstack",
238
+ "sjv.io": "impact",
239
+ "pxf.io": "impact",
240
+ "ojrq.net": "impact",
241
+ "7eer.net": "impact",
242
+ "shareasale.com": "shareasale",
243
+ "awin1.com": "awin",
244
+ "dpbolvw.net": "cj",
245
+ "anrdoezrs.net": "cj",
246
+ "kqzyfj.com": "cj",
247
+ "tkqlhce.com": "cj",
248
+ "jdoqocy.com": "cj",
249
+ }
250
+
251
+
252
+ def _program_from_host(host: str) -> str:
253
+ labels = [x for x in host.split(".") if x]
254
+ if len(labels) >= 2:
255
+ return labels[-2]
256
+ return host
257
+
258
+
259
+ def affiliate_info(url: str) -> dict | None:
260
+ """Describe ``url`` as an affiliate link, or None when it does not look like one.
261
+
262
+ ``program`` is the product the link promotes: the subdomain on a tracking host
263
+ (``acme.partnerlinks.io`` -> ``acme``), else the site's own name
264
+ (``www.acme.com/?via=x`` -> ``acme``).
265
+ """
266
+ host = _host(url)
267
+ if not host:
268
+ return None
269
+ for tracker, network in AFFILIATE_HOSTS.items():
270
+ if host == tracker or host.endswith("." + tracker):
271
+ sub = host[: -len(tracker)].rstrip(".")
272
+ program = sub.split(".")[-1] if sub else tracker
273
+ return {"url": url, "host": host, "network": network, "program": program, "param": None}
274
+ try:
275
+ params = parse_qsl(urlsplit(url).query, keep_blank_values=False)
276
+ except ValueError:
277
+ params = []
278
+ for key, value in params:
279
+ k = key.lower()
280
+ if k in AFFILIATE_PARAMS and value.strip():
281
+ network = AFFILIATE_PARAMS[k]
282
+ if k == "tag" and "amazon." not in host:
283
+ continue
284
+ return {"url": url, "host": host, "network": network,
285
+ "program": _program_from_host(host), "param": k}
286
+ return None
287
+
288
+
289
+ # --- one-shot extraction ------------------------------------------------------
290
+
291
+ def empty_result() -> dict:
292
+ return {"emails": [], "links": {k: [] for k in LINK_KINDS}, "affiliate_links": []}
293
+
294
+
295
+ def extract(text: str) -> dict:
296
+ """Emails, links grouped by kind, and affiliate-shaped links found in ``text``."""
297
+ out = empty_result()
298
+ out["emails"] = find_emails(text)
299
+ for url in find_urls(text):
300
+ kind = classify_url(url)
301
+ aff = affiliate_info(url)
302
+ if aff:
303
+ out["affiliate_links"].append(aff)
304
+ # An affiliate link points at someone else's product, not the
305
+ # person's own site: never let it pose as their website.
306
+ if kind == "website":
307
+ kind = "other"
308
+ out["links"][kind].append(url)
309
+ return out
310
+
311
+
312
+ def merge(*results: dict) -> dict:
313
+ """Union of several ``extract`` results, order-preserving and de-duplicated."""
314
+ out = empty_result()
315
+ for r in results:
316
+ for e in r.get("emails", []):
317
+ if e not in out["emails"]:
318
+ out["emails"].append(e)
319
+ for kind, urls in (r.get("links") or {}).items():
320
+ bucket = out["links"].setdefault(kind, [])
321
+ for u in urls:
322
+ if u not in bucket:
323
+ bucket.append(u)
324
+ seen = {a["url"] for a in out["affiliate_links"]}
325
+ for a in r.get("affiliate_links", []):
326
+ if a["url"] not in seen:
327
+ out["affiliate_links"].append(a)
328
+ seen.add(a["url"])
329
+ return out
330
+
331
+
332
+ # --- HTML pages ---------------------------------------------------------------
333
+
334
+ class _PageParser(HTMLParser):
335
+ _SKIP = {"script", "style", "noscript", "svg", "template"}
336
+
337
+ def __init__(self):
338
+ super().__init__(convert_charrefs=True)
339
+ self.hrefs: list[str] = []
340
+ self.text: list[str] = []
341
+ self.title = ""
342
+ self._skip_depth = 0
343
+ self._in_title = False
344
+
345
+ def handle_starttag(self, tag, attrs):
346
+ if tag in self._SKIP:
347
+ self._skip_depth += 1
348
+ if tag == "title":
349
+ self._in_title = True
350
+ if tag == "a":
351
+ for name, value in attrs:
352
+ if name == "href" and value:
353
+ self.hrefs.append(value.strip())
354
+
355
+ def handle_endtag(self, tag):
356
+ if tag in self._SKIP and self._skip_depth:
357
+ self._skip_depth -= 1
358
+ if tag == "title":
359
+ self._in_title = False
360
+
361
+ def handle_data(self, data):
362
+ if self._in_title:
363
+ self.title += data
364
+ elif not self._skip_depth:
365
+ self.text.append(data)
366
+
367
+
368
+ # Kinds safe to take from anywhere in a page's source: a profile URL is a profile
369
+ # whether it sits in an anchor or in a JSON blob. Plain sites, link-in-bio pages and
370
+ # affiliate links are NOT taken from raw source: link-in-bio services embed their
371
+ # own assets and sponsored offers there, and those are not the creator's.
372
+ _RAW_SAFE_KINDS = ("linkedin", "x", "youtube", "instagram", "tiktok", "facebook",
373
+ "github", "booking", "newsletter")
374
+
375
+ _NEXT_DATA_RE = re.compile(
376
+ r"""<script[^>]*id=["']__NEXT_DATA__["'][^>]*>(.*?)</script>""",
377
+ re.IGNORECASE | re.DOTALL)
378
+
379
+
380
+ def _site(host: str) -> str:
381
+ labels = [x for x in (host or "").split(".") if x]
382
+ return ".".join(labels[-2:])
383
+
384
+
385
+ def next_data_links(page: str) -> tuple[list[str], list[str]]:
386
+ """Links a creator chose, from a Next.js page's ``__NEXT_DATA__`` JSON.
387
+
388
+ Link-in-bio services built on Next.js (Linktree among them) render no anchors
389
+ server-side: the creator's buttons live in ``props.pageProps.links[].url``, the
390
+ social icons in ``socialLinks[].url`` (``pinnedLinks`` too) and the bio in
391
+ ``pageProps.description``. Returns ``(urls, texts)``, empty when the page has no
392
+ such blob. Sponsored offers the platform injects sit elsewhere in the same JSON
393
+ and are deliberately not read.
394
+ """
395
+ m = _NEXT_DATA_RE.search(page or "")
396
+ if not m:
397
+ return [], []
398
+ try:
399
+ data = json.loads(m.group(1))
400
+ except ValueError:
401
+ return [], []
402
+ props = data.get("props") if isinstance(data, dict) else None
403
+ pp = props.get("pageProps") if isinstance(props, dict) else None
404
+ if not isinstance(pp, dict):
405
+ return [], []
406
+ urls: list[str] = []
407
+ for k in ("links", "socialLinks", "pinnedLinks"):
408
+ items = pp.get(k)
409
+ for item in items if isinstance(items, list) else []:
410
+ if isinstance(item, dict) and isinstance(item.get("url"), str):
411
+ urls.append(item["url"].strip())
412
+ texts = [pp["description"]] if isinstance(pp.get("description"), str) else []
413
+ return urls, texts
414
+
415
+
416
+ def _split_hrefs(hrefs: list[str], base_url: str) -> tuple[list[str], list[str]]:
417
+ """(emails from mailto: links, absolute http(s) URLs)."""
418
+ mailto: list[str] = []
419
+ urls: list[str] = []
420
+ for h in hrefs:
421
+ h = _html.unescape(h).strip()
422
+ low = h.lower()
423
+ if low.startswith("mailto:"):
424
+ mailto.extend(find_emails(h[7:].split("?", 1)[0]))
425
+ elif low.startswith(("http://", "https://")):
426
+ urls.append(h)
427
+ elif h.startswith("/") and base_url:
428
+ urls.append(urljoin(base_url, h))
429
+ return mailto, urls
430
+
431
+
432
+ def extract_html(page: str, base_url: str = "") -> dict:
433
+ """Contacts on an HTML page, from three places:
434
+
435
+ 1. ``mailto:`` links, anchor hrefs and visible text (what a visitor sees);
436
+ 2. the links a creator chose on a Next.js link-in-bio page (``next_data_links``);
437
+ 3. profile URLs (LinkedIn, X, Instagram, ...) anywhere in the raw source.
438
+
439
+ Links back to the page's own site are dropped from the link-in-bio / website /
440
+ other buckets: on a link-in-bio page they are navigation and assets, not the
441
+ creator's destinations.
442
+ """
443
+ parser = _PageParser()
444
+ try:
445
+ parser.feed(page or "")
446
+ parser.close()
447
+ except Exception: # noqa: BLE001 - a malformed page still yields what was parsed
448
+ pass
449
+
450
+ mailto, hrefs = _split_hrefs(parser.hrefs, base_url)
451
+ visible = " ".join(t.strip() for t in parser.text if t.strip())
452
+ shown = merge({"emails": mailto, "links": {}, "affiliate_links": []},
453
+ extract(visible + "\n" + "\n".join(hrefs)))
454
+
455
+ nd_urls, nd_texts = next_data_links(page)
456
+ nd_mailto, nd_hrefs = _split_hrefs(nd_urls, base_url)
457
+ chosen = merge({"emails": nd_mailto, "links": {}, "affiliate_links": []},
458
+ extract("\n".join(nd_texts + nd_hrefs)))
459
+
460
+ raw_all = extract(_html.unescape(page or ""))
461
+ raw = {"emails": [], "affiliate_links": [],
462
+ "links": {k: (v if k in _RAW_SAFE_KINDS else []) for k, v in raw_all["links"].items()}}
463
+
464
+ result = merge(shown, chosen, raw)
465
+ site = _site(_host(base_url)) if base_url else ""
466
+ if site:
467
+ for kind in ("link_in_bio", "website", "other"):
468
+ result["links"][kind] = [u for u in result["links"].get(kind, [])
469
+ if _site(_host(u)) != site]
470
+ result["title"] = " ".join(parser.title.split())
471
+ return result
472
+
473
+
474
+ def collect(sources: list[tuple[str, str]]) -> dict:
475
+ """Extract from several labelled texts, keeping where each find came from.
476
+
477
+ ``sources`` is ``[(source_label, text), ...]`` (e.g. ``("video:abc", desc)``).
478
+ Returns emails as ``[{"email", "sources": [...]}]``, links grouped by kind, and
479
+ affiliate links each carrying the ``source`` it was first seen in.
480
+ """
481
+ emails: dict[str, list[str]] = {}
482
+ merged = empty_result()
483
+ affiliates: list[dict] = []
484
+ seen: set[str] = set()
485
+ for source, text in sources:
486
+ r = extract(text or "")
487
+ for e in r["emails"]:
488
+ emails.setdefault(e, [])
489
+ if source not in emails[e]:
490
+ emails[e].append(source)
491
+ merged = merge(merged, {"emails": [], "links": r["links"], "affiliate_links": []})
492
+ for a in r["affiliate_links"]:
493
+ if a["url"] not in seen:
494
+ affiliates.append({**a, "source": source})
495
+ seen.add(a["url"])
496
+ kept = drop_shadowed(list(emails))
497
+ return {
498
+ "emails": [{"email": e, "sources": emails[e]} for e in kept],
499
+ "links": merged["links"],
500
+ "affiliate_links": affiliates,
501
+ "sources_scanned": len(sources),
502
+ }
503
+
504
+
505
+ # --- LinkedIn profiles found by a person's name -------------------------------
506
+
507
+ def fold(text: str) -> str:
508
+ """Lowercase ASCII fold: accents dropped, so 'Ștefan' and 'stefan' compare equal."""
509
+ return "".join(c for c in unicodedata.normalize("NFKD", text or "")
510
+ if not unicodedata.combining(c)).lower()
511
+
512
+
513
+ def linkedin_slug(url: str) -> str:
514
+ """The profile id in a linkedin.com/in/<slug> URL ('' when it is not one)."""
515
+ try:
516
+ parts = urlsplit(url if "://" in url else "https://" + url)
517
+ except ValueError:
518
+ return ""
519
+ if not _host_matches(_host(url if "://" in url else "https://" + url), ("linkedin.com",)):
520
+ return ""
521
+ segs = [x for x in parts.path.split("/") if x]
522
+ if len(segs) >= 2 and segs[0].lower() == "in":
523
+ return unquote(segs[1])
524
+ return ""
525
+
526
+
527
+ def normalize_linkedin_profile(url: str) -> str:
528
+ slug = linkedin_slug(url)
529
+ return f"https://www.linkedin.com/in/{slug}" if slug else ""
530
+
531
+
532
+ def linkedin_slug_has_name(url: str, name: str) -> bool:
533
+ """True when the profile id contains the FULL name: every name token, in order
534
+ (or reversed), joined by nothing, '-', '_' or '.' (``beau-carnes``,
535
+ ``beaucarnes``, ``carnes-beau-12ab``). A single-word name never matches: one word
536
+ identifies nobody."""
537
+ slug = fold(linkedin_slug(url))
538
+ tokens = [t for t in re.split(r"[^a-z0-9]+", fold(name)) if t]
539
+ if not slug or len(tokens) < 2:
540
+ return False
541
+ sep = r"[-_.]?"
542
+ forward = sep.join(map(re.escape, tokens))
543
+ backward = sep.join(map(re.escape, reversed(tokens)))
544
+ return bool(re.search(forward, slug) or re.search(backward, slug))
@@ -0,0 +1,83 @@
1
+ """Credential access - a facade over a pluggable backend (see ``creds_backends``).
2
+
3
+ Public API is unchanged: ``get`` / ``get_optional`` / ``has`` / ``set_secret`` /
4
+ ``delete``. The backend is chosen once by the ``AAT_CREDS_BACKEND`` env var; the
5
+ default (unset) is ``windows`` = Windows Credential Manager, the historical
6
+ behaviour, so a local Claude Code session is never affected. Import-safe on any OS:
7
+ the Windows/keyring code loads only when the windows backend is selected, so this
8
+ module imports cleanly inside a Linux container.
9
+
10
+ Credentials are identified as ``agents-and-tools:<tool>:<secret>``.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import os
15
+
16
+ from tools._lib import creds_backends
17
+
18
+ NAMESPACE = "agents-and-tools"
19
+
20
+ _backend = None
21
+
22
+
23
+ def _service(tool: str) -> str:
24
+ return f"{NAMESPACE}:{tool}"
25
+
26
+
27
+ def _b():
28
+ global _backend
29
+ if _backend is None:
30
+ _backend = creds_backends.make(os.environ.get("AAT_CREDS_BACKEND"))
31
+ return _backend
32
+
33
+
34
+ def reset_backend() -> None:
35
+ """Drop the cached backend. For tests, or after changing AAT_CREDS_BACKEND."""
36
+ global _backend
37
+ _backend = None
38
+
39
+
40
+ def get(tool: str, secret_name: str) -> str:
41
+ """Return the secret value. Raises KeyError if missing."""
42
+ value = _b().get_optional(tool, secret_name)
43
+ if value is None:
44
+ raise KeyError(f"missing credential: {_service(tool)} / {secret_name}")
45
+ return value
46
+
47
+
48
+ def get_optional(tool: str, secret_name: str) -> str | None:
49
+ """Return the secret value or None if missing. Never raises on absence."""
50
+ return _b().get_optional(tool, secret_name)
51
+
52
+
53
+ def has(tool: str, secret_name: str) -> bool:
54
+ return _b().get_optional(tool, secret_name) is not None
55
+
56
+
57
+ def has_for_report(tool: str, secret_name: str) -> bool:
58
+ """Presence, for a listing or a view. NEVER raises.
59
+
60
+ Use this and only this where the answer decorates output: a `has_password`
61
+ field, a registry's `ready` flag, a dashboard tile. On a machine with no
62
+ reachable credential store - a headless Linux box has no session keyring at
63
+ all - constructing the backend throws, and a reporting call that propagates
64
+ that takes down the whole listing with it. Unreachable reports False, which
65
+ is the honest answer to "can I show a credential here": no.
66
+
67
+ Do NOT use it where the value is then acted on. `get` and `get_optional`
68
+ still raise on an unreachable store on purpose, because a store that cannot
69
+ be read must never be mistaken for a store that is empty; that is how a
70
+ caller ends up storing a secret again under the wrong key.
71
+ """
72
+ try:
73
+ return has(tool, secret_name)
74
+ except Exception: # noqa: BLE001 - see docstring
75
+ return False
76
+
77
+
78
+ def set_secret(tool: str, secret_name: str, value: str) -> None:
79
+ _b().set(tool, secret_name, value)
80
+
81
+
82
+ def delete(tool: str, secret_name: str) -> None:
83
+ _b().delete(tool, secret_name)