pycatalyst 0.0.2__tar.gz → 0.0.7.dev0__tar.gz

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 (410) hide show
  1. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/.github/workflows/publish.yml +1 -1
  2. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/.github/workflows/test.yml +5 -5
  3. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/.gitignore +15 -0
  4. {pycatalyst-0.0.2/src/pycatalyst.egg-info → pycatalyst-0.0.7.dev0}/PKG-INFO +7 -2
  5. pycatalyst-0.0.7.dev0/pycatalyst.cfg +67 -0
  6. pycatalyst-0.0.7.dev0/pycatalyst.cfg.example +67 -0
  7. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/pyproject.toml +13 -1
  8. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/setup.sh +0 -0
  9. pycatalyst-0.0.7.dev0/src/pycatalyst/api/dependencies/__init__.py +8 -0
  10. pycatalyst-0.0.7.dev0/src/pycatalyst/api/dependencies/auth.py +143 -0
  11. pycatalyst-0.0.7.dev0/src/pycatalyst/api/dependencies/database.py +131 -0
  12. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/api/main.py +66 -7
  13. pycatalyst-0.0.7.dev0/src/pycatalyst/api/models/__init__.py +8 -0
  14. pycatalyst-0.0.7.dev0/src/pycatalyst/api/models/requests.py +12 -0
  15. pycatalyst-0.0.7.dev0/src/pycatalyst/api/models/responses.py +20 -0
  16. pycatalyst-0.0.7.dev0/src/pycatalyst/api/routes/v1/auth.py +85 -0
  17. pycatalyst-0.0.7.dev0/src/pycatalyst/api/routes/v1/settings.py +27 -0
  18. pycatalyst-0.0.7.dev0/src/pycatalyst/api/routes/v1/workbench.py +528 -0
  19. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/cli.py +33 -3
  20. pycatalyst-0.0.7.dev0/src/pycatalyst/config/__init__.py +37 -0
  21. pycatalyst-0.0.7.dev0/src/pycatalyst/config/auth.py +68 -0
  22. pycatalyst-0.0.7.dev0/src/pycatalyst/config/database.py +36 -0
  23. pycatalyst-0.0.7.dev0/src/pycatalyst/config/paths.py +56 -0
  24. pycatalyst-0.0.7.dev0/src/pycatalyst/config/ui.py +56 -0
  25. pycatalyst-0.0.7.dev0/src/pycatalyst/config/workbench.py +147 -0
  26. pycatalyst-0.0.7.dev0/src/pycatalyst/data/seed/.gitkeep +1 -0
  27. pycatalyst-0.0.7.dev0/src/pycatalyst/db/__init__.py +26 -0
  28. pycatalyst-0.0.7.dev0/src/pycatalyst/db/base.py +62 -0
  29. pycatalyst-0.0.7.dev0/src/pycatalyst/db/cli.py +531 -0
  30. pycatalyst-0.0.7.dev0/src/pycatalyst/db/config.py +109 -0
  31. pycatalyst-0.0.7.dev0/src/pycatalyst/db/migrations/__init__.py +1 -0
  32. pycatalyst-0.0.7.dev0/src/pycatalyst/db/migrations/env.py +62 -0
  33. pycatalyst-0.0.7.dev0/src/pycatalyst/db/migrations/versions/20260101000000_initial.py +53 -0
  34. pycatalyst-0.0.7.dev0/src/pycatalyst/db/migrations/versions/__init__.py +1 -0
  35. pycatalyst-0.0.7.dev0/src/pycatalyst/db/models/__init__.py +7 -0
  36. pycatalyst-0.0.7.dev0/src/pycatalyst/db/models/app_metadata.py +21 -0
  37. pycatalyst-0.0.7.dev0/src/pycatalyst/db/paths.py +35 -0
  38. pycatalyst-0.0.7.dev0/src/pycatalyst/services/__init__.py +1 -0
  39. pycatalyst-0.0.7.dev0/src/pycatalyst/services/backends/__init__.py +5 -0
  40. pycatalyst-0.0.7.dev0/src/pycatalyst/services/backends/base.py +152 -0
  41. pycatalyst-0.0.7.dev0/src/pycatalyst/services/backends/docker_backend.py +283 -0
  42. pycatalyst-0.0.7.dev0/src/pycatalyst/services/backends/venv_backend.py +305 -0
  43. pycatalyst-0.0.7.dev0/src/pycatalyst/services/workbench_manager.py +400 -0
  44. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/.eslintrc.json +3 -0
  45. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/.npmrc +3 -0
  46. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/__init__.py +3 -0
  47. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/build.py +42 -0
  48. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/components.json +8 -0
  49. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/dev.py +40 -0
  50. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/next-env.d.ts +6 -0
  51. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/next.config.js +16 -0
  52. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/node_modules/flatted/python/flatted.py +149 -0
  53. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/404/index.html +1 -0
  54. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/404.html +1 -0
  55. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/__next.__PAGE__.txt +9 -0
  56. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/__next._full.txt +22 -0
  57. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/__next._head.txt +5 -0
  58. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/__next._index.txt +10 -0
  59. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/__next._tree.txt +2 -0
  60. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/0575f275776d153c.js +1 -0
  61. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/0664a23cd6a5f7ba.js +34 -0
  62. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/31674d9a34dac40b.js +2 -0
  63. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/33dc0ae90f863979.js +1 -0
  64. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/3db2622c9e17684f.js +1 -0
  65. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/48f408d5c9d2a0ba.js +1 -0
  66. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/4a93e9c0c43d24a7.css +2 -0
  67. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/612195d87a9f708e.js +1 -0
  68. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/826946f349a354cd.js +24 -0
  69. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/82abf2d65f5428ae.js +4 -0
  70. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/88a6dfcd964f4302.js +73 -0
  71. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  72. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
  73. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/ab52a6590efbe138.js +1 -0
  74. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/ace5f280042c2f7a.js +1 -0
  75. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/b7b85a15f387ab7c.css +1 -0
  76. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/b82d3555a0c5ad54.js +1 -0
  77. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/c0645d6fea057384.js +1 -0
  78. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/c6ac0e3a3d0e355f.js +5 -0
  79. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/cc784667b24a8c60.js +1 -0
  80. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/cd046009fa99a225.js +1 -0
  81. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/d2be314c3ece3fbe.js +1 -0
  82. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/de634c2407095732.js +5 -0
  83. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/f2f58a7e93290fbb.js +1 -0
  84. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/ff1a16fafef87110.js +1 -0
  85. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/chunks/turbopack-dc3a2def751ab70b.js +4 -0
  86. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/xwVOo753r2AQr1Ieeepou/_buildManifest.js +11 -0
  87. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/xwVOo753r2AQr1Ieeepou/_clientMiddlewareManifest.json +1 -0
  88. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_next/static/xwVOo753r2AQr1Ieeepou/_ssgManifest.js +1 -0
  89. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_not-found/__next._full.txt +19 -0
  90. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_not-found/__next._head.txt +5 -0
  91. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_not-found/__next._index.txt +10 -0
  92. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_not-found/__next._not-found.__PAGE__.txt +5 -0
  93. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_not-found/__next._not-found.txt +4 -0
  94. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_not-found/__next._tree.txt +2 -0
  95. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_not-found/index.html +1 -0
  96. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/_not-found/index.txt +19 -0
  97. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/documentation/__next._full.txt +22 -0
  98. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/documentation/__next._head.txt +5 -0
  99. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/documentation/__next._index.txt +10 -0
  100. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/documentation/__next._tree.txt +2 -0
  101. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/documentation/__next.documentation.__PAGE__.txt +9 -0
  102. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/documentation/__next.documentation.txt +4 -0
  103. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/documentation/index.html +1 -0
  104. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/documentation/index.txt +22 -0
  105. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/generate/__next._full.txt +22 -0
  106. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/generate/__next._head.txt +5 -0
  107. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/generate/__next._index.txt +10 -0
  108. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/generate/__next._tree.txt +2 -0
  109. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/generate/__next.generate.__PAGE__.txt +9 -0
  110. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/generate/__next.generate.txt +4 -0
  111. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/generate/index.html +1 -0
  112. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/generate/index.txt +22 -0
  113. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/index.html +1 -0
  114. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/index.txt +22 -0
  115. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/login/__next._full.txt +22 -0
  116. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/login/__next._head.txt +5 -0
  117. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/login/__next._index.txt +10 -0
  118. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/login/__next._tree.txt +2 -0
  119. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/login/__next.login.__PAGE__.txt +9 -0
  120. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/login/__next.login.txt +4 -0
  121. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/login/index.html +1 -0
  122. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/login/index.txt +22 -0
  123. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/recipes/__next._full.txt +22 -0
  124. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/recipes/__next._head.txt +5 -0
  125. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/recipes/__next._index.txt +10 -0
  126. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/recipes/__next._tree.txt +2 -0
  127. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/recipes/__next.recipes.__PAGE__.txt +9 -0
  128. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/recipes/__next.recipes.txt +4 -0
  129. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/recipes/index.html +1 -0
  130. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/recipes/index.txt +22 -0
  131. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/schemas/__next._full.txt +22 -0
  132. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/schemas/__next._head.txt +5 -0
  133. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/schemas/__next._index.txt +10 -0
  134. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/schemas/__next._tree.txt +2 -0
  135. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/schemas/__next.schemas.__PAGE__.txt +9 -0
  136. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/schemas/__next.schemas.txt +4 -0
  137. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/schemas/index.html +1 -0
  138. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/schemas/index.txt +22 -0
  139. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/settings/__next._full.txt +22 -0
  140. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/settings/__next._head.txt +5 -0
  141. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/settings/__next._index.txt +10 -0
  142. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/settings/__next._tree.txt +2 -0
  143. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/settings/__next.settings.__PAGE__.txt +9 -0
  144. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/settings/__next.settings.txt +4 -0
  145. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/settings/index.html +1 -0
  146. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/settings/index.txt +22 -0
  147. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/sinks/__next._full.txt +22 -0
  148. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/sinks/__next._head.txt +5 -0
  149. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/sinks/__next._index.txt +10 -0
  150. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/sinks/__next._tree.txt +2 -0
  151. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/sinks/__next.sinks.__PAGE__.txt +9 -0
  152. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/sinks/__next.sinks.txt +4 -0
  153. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/sinks/index.html +1 -0
  154. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/sinks/index.txt +22 -0
  155. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/workbench/__next._full.txt +23 -0
  156. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/workbench/__next._head.txt +5 -0
  157. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/workbench/__next._index.txt +10 -0
  158. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/workbench/__next._tree.txt +3 -0
  159. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/workbench/__next.workbench.__PAGE__.txt +10 -0
  160. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/workbench/__next.workbench.txt +4 -0
  161. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/workbench/index.html +1 -0
  162. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/out/workbench/index.txt +23 -0
  163. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/package-lock.json +6687 -0
  164. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/package.json +39 -0
  165. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/postcss.config.js +6 -0
  166. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/server.py +218 -0
  167. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/documentation/page.tsx +212 -0
  168. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/error.tsx +31 -0
  169. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/generate/page.tsx +303 -0
  170. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/globals.css +80 -0
  171. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/layout.tsx +39 -0
  172. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/loading.tsx +5 -0
  173. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/login/page.tsx +102 -0
  174. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/page.tsx +93 -0
  175. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/recipes/page.tsx +220 -0
  176. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/schemas/page.tsx +210 -0
  177. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/settings/page.tsx +102 -0
  178. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/sinks/page.tsx +125 -0
  179. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/themes.css +183 -0
  180. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/app/workbench/page.tsx +626 -0
  181. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ThemeFromConfig.tsx +41 -0
  182. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ThemeSync.tsx +46 -0
  183. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/common/ErrorBoundary.tsx +41 -0
  184. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/common/ErrorDisplay.tsx +19 -0
  185. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/common/LoadingSpinner.tsx +15 -0
  186. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/common/index.ts +3 -0
  187. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/layout/ApiUnavailableBanner.tsx +20 -0
  188. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/layout/AppShell.tsx +20 -0
  189. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/layout/AuthGuard.tsx +35 -0
  190. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/layout/Navigation.tsx +187 -0
  191. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/layout/PageContainer.tsx +20 -0
  192. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/layout/PageHeader.tsx +19 -0
  193. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/layout/index.ts +6 -0
  194. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/alert.tsx +58 -0
  195. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/badge.tsx +32 -0
  196. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/button.tsx +54 -0
  197. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/card.tsx +79 -0
  198. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/input.tsx +24 -0
  199. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/label.tsx +23 -0
  200. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/skeleton.tsx +15 -0
  201. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/switch.tsx +43 -0
  202. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/tabs.tsx +94 -0
  203. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/ui/textarea.tsx +23 -0
  204. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/workbench/ContainerDialog.tsx +70 -0
  205. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/workbench/TerminalPanel.tsx +337 -0
  206. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/components/workbench/WorkbenchToolbar.tsx +219 -0
  207. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/hooks/use-auth-init.ts +24 -0
  208. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/api.ts +155 -0
  209. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/auth-storage.ts +26 -0
  210. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/constants.ts +42 -0
  211. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/settings.ts +46 -0
  212. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/terminal-theme.ts +131 -0
  213. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/types/index.ts +71 -0
  214. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/utils.ts +92 -0
  215. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/workbench-storage.ts +74 -0
  216. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/lib/workbench.ts +191 -0
  217. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/stores/app-config.ts +21 -0
  218. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/stores/auth.ts +76 -0
  219. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/stores/index.ts +2 -0
  220. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/src/types/global.d.ts +11 -0
  221. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/404/index.html +1 -0
  222. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/404.html +1 -0
  223. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/__next.__PAGE__.txt +9 -0
  224. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/__next._full.txt +22 -0
  225. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/__next._head.txt +5 -0
  226. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/__next._index.txt +10 -0
  227. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/__next._tree.txt +2 -0
  228. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/0575f275776d153c.js +1 -0
  229. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/0664a23cd6a5f7ba.js +34 -0
  230. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/31674d9a34dac40b.js +2 -0
  231. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/33dc0ae90f863979.js +1 -0
  232. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/3db2622c9e17684f.js +1 -0
  233. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/48f408d5c9d2a0ba.js +1 -0
  234. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/4a93e9c0c43d24a7.css +2 -0
  235. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/612195d87a9f708e.js +1 -0
  236. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/826946f349a354cd.js +24 -0
  237. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/82abf2d65f5428ae.js +4 -0
  238. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/88a6dfcd964f4302.js +73 -0
  239. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  240. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/a6dad97d9634a72d.js.map +1 -0
  241. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/ab52a6590efbe138.js +1 -0
  242. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/ace5f280042c2f7a.js +1 -0
  243. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/b7b85a15f387ab7c.css +1 -0
  244. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/b82d3555a0c5ad54.js +1 -0
  245. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/c0645d6fea057384.js +1 -0
  246. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/c6ac0e3a3d0e355f.js +5 -0
  247. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/cc784667b24a8c60.js +1 -0
  248. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/cd046009fa99a225.js +1 -0
  249. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/d2be314c3ece3fbe.js +1 -0
  250. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/de634c2407095732.js +5 -0
  251. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/f2f58a7e93290fbb.js +1 -0
  252. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
  253. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/chunks/turbopack-dc3a2def751ab70b.js +4 -0
  254. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/xwVOo753r2AQr1Ieeepou/_buildManifest.js +11 -0
  255. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/xwVOo753r2AQr1Ieeepou/_clientMiddlewareManifest.json +1 -0
  256. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_next/static/xwVOo753r2AQr1Ieeepou/_ssgManifest.js +1 -0
  257. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_not-found/__next._full.txt +19 -0
  258. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_not-found/__next._head.txt +5 -0
  259. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_not-found/__next._index.txt +10 -0
  260. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
  261. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_not-found/__next._not-found.txt +4 -0
  262. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_not-found/__next._tree.txt +2 -0
  263. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_not-found/index.html +1 -0
  264. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/_not-found/index.txt +19 -0
  265. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/documentation/__next._full.txt +22 -0
  266. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/documentation/__next._head.txt +5 -0
  267. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/documentation/__next._index.txt +10 -0
  268. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/documentation/__next._tree.txt +2 -0
  269. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/documentation/__next.documentation.__PAGE__.txt +9 -0
  270. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/documentation/__next.documentation.txt +4 -0
  271. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/documentation/index.html +1 -0
  272. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/documentation/index.txt +22 -0
  273. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/generate/__next._full.txt +22 -0
  274. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/generate/__next._head.txt +5 -0
  275. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/generate/__next._index.txt +10 -0
  276. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/generate/__next._tree.txt +2 -0
  277. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/generate/__next.generate.__PAGE__.txt +9 -0
  278. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/generate/__next.generate.txt +4 -0
  279. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/generate/index.html +1 -0
  280. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/generate/index.txt +22 -0
  281. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/index.html +1 -0
  282. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/index.txt +22 -0
  283. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/login/__next._full.txt +22 -0
  284. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/login/__next._head.txt +5 -0
  285. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/login/__next._index.txt +10 -0
  286. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/login/__next._tree.txt +2 -0
  287. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/login/__next.login.__PAGE__.txt +9 -0
  288. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/login/__next.login.txt +4 -0
  289. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/login/index.html +1 -0
  290. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/login/index.txt +22 -0
  291. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/recipes/__next._full.txt +22 -0
  292. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/recipes/__next._head.txt +5 -0
  293. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/recipes/__next._index.txt +10 -0
  294. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/recipes/__next._tree.txt +2 -0
  295. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/recipes/__next.recipes.__PAGE__.txt +9 -0
  296. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/recipes/__next.recipes.txt +4 -0
  297. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/recipes/index.html +1 -0
  298. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/recipes/index.txt +22 -0
  299. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/schemas/__next._full.txt +22 -0
  300. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/schemas/__next._head.txt +5 -0
  301. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/schemas/__next._index.txt +10 -0
  302. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/schemas/__next._tree.txt +2 -0
  303. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/schemas/__next.schemas.__PAGE__.txt +9 -0
  304. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/schemas/__next.schemas.txt +4 -0
  305. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/schemas/index.html +1 -0
  306. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/schemas/index.txt +22 -0
  307. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/settings/__next._full.txt +22 -0
  308. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/settings/__next._head.txt +5 -0
  309. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/settings/__next._index.txt +10 -0
  310. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/settings/__next._tree.txt +2 -0
  311. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/settings/__next.settings.__PAGE__.txt +9 -0
  312. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/settings/__next.settings.txt +4 -0
  313. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/settings/index.html +1 -0
  314. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/settings/index.txt +22 -0
  315. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/sinks/__next._full.txt +22 -0
  316. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/sinks/__next._head.txt +5 -0
  317. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/sinks/__next._index.txt +10 -0
  318. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/sinks/__next._tree.txt +2 -0
  319. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/sinks/__next.sinks.__PAGE__.txt +9 -0
  320. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/sinks/__next.sinks.txt +4 -0
  321. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/sinks/index.html +1 -0
  322. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/sinks/index.txt +22 -0
  323. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/workbench/__next._full.txt +23 -0
  324. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/workbench/__next._head.txt +5 -0
  325. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/workbench/__next._index.txt +10 -0
  326. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/workbench/__next._tree.txt +3 -0
  327. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/workbench/__next.workbench.__PAGE__.txt +10 -0
  328. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/workbench/__next.workbench.txt +4 -0
  329. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/workbench/index.html +1 -0
  330. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/static/workbench/index.txt +23 -0
  331. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/tailwind.config.js +74 -0
  332. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/tsconfig.json +42 -0
  333. pycatalyst-0.0.7.dev0/src/pycatalyst/ui/tsconfig.tsbuildinfo +1 -0
  334. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0/src/pycatalyst.egg-info}/PKG-INFO +7 -2
  335. pycatalyst-0.0.7.dev0/src/pycatalyst.egg-info/SOURCES.txt +404 -0
  336. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst.egg-info/requires.txt +8 -1
  337. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_api.py +10 -4
  338. pycatalyst-0.0.7.dev0/tests/unit/test_scrollback_buffer.py +77 -0
  339. pycatalyst-0.0.7.dev0/tests/unit/test_venv_backend.py +223 -0
  340. pycatalyst-0.0.7.dev0/tests/unit/test_workbench_manager.py +319 -0
  341. pycatalyst-0.0.7.dev0/tests/unit/test_workbench_routes.py +378 -0
  342. pycatalyst-0.0.2/src/pycatalyst/worker/__init__.py +0 -0
  343. pycatalyst-0.0.2/src/pycatalyst.egg-info/SOURCES.txt +0 -78
  344. pycatalyst-0.0.2/tests/integration/__init__.py +0 -0
  345. pycatalyst-0.0.2/tests/unit/__init__.py +0 -0
  346. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/.pre-commit-config.yaml +0 -0
  347. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/CHANGELOG.md +0 -0
  348. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/CONTRIBUTING.md +0 -0
  349. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/LICENSE +0 -0
  350. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/Makefile +0 -0
  351. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/README.md +0 -0
  352. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/docs/PUBLISHING.md +0 -0
  353. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/scripts/publish-once.sh +0 -0
  354. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/setup.cfg +0 -0
  355. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/__init__.py +0 -0
  356. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/_protocols.py +0 -0
  357. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/_types.py +0 -0
  358. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/api/__init__.py +0 -0
  359. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/api/routes/__init__.py +0 -0
  360. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/api/routes/v1/__init__.py +0 -0
  361. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/api/routes/v1/generate.py +0 -0
  362. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/api/routes/v1/infer.py +0 -0
  363. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/api/routes/v1/recipes.py +0 -0
  364. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/data/templates/recipes/pycharter_contracts.yaml +0 -0
  365. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/data/templates/recipes/pyoptima_portfolio.yaml +0 -0
  366. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/data/templates/recipes/pystator_events.yaml +0 -0
  367. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/errors.py +0 -0
  368. {pycatalyst-0.0.2/src/pycatalyst/config → pycatalyst-0.0.7.dev0/src/pycatalyst/generators}/__init__.py +0 -0
  369. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/generators/compound.py +0 -0
  370. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/generators/engine.py +0 -0
  371. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/generators/registry.py +0 -0
  372. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/generators/scalar.py +0 -0
  373. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/py.typed +0 -0
  374. {pycatalyst-0.0.2/src/pycatalyst/db → pycatalyst-0.0.7.dev0/src/pycatalyst/recipes}/__init__.py +0 -0
  375. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/recipes/loader.py +0 -0
  376. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/recipes/models.py +0 -0
  377. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/recipes/runner.py +0 -0
  378. {pycatalyst-0.0.2/src/pycatalyst/generators → pycatalyst-0.0.7.dev0/src/pycatalyst/schema}/__init__.py +0 -0
  379. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/schema/inferrer.py +0 -0
  380. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/schema/json_schema.py +0 -0
  381. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/schema/pydantic.py +0 -0
  382. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/schema/spec.py +0 -0
  383. {pycatalyst-0.0.2/src/pycatalyst/recipes → pycatalyst-0.0.7.dev0/src/pycatalyst/sinks}/__init__.py +0 -0
  384. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/sinks/database.py +0 -0
  385. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/sinks/file.py +0 -0
  386. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/sinks/http.py +0 -0
  387. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/sinks/kafka.py +0 -0
  388. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/sinks/memory.py +0 -0
  389. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst/sinks/rabbitmq.py +0 -0
  390. {pycatalyst-0.0.2/src/pycatalyst/schema → pycatalyst-0.0.7.dev0/src/pycatalyst/worker}/__init__.py +0 -0
  391. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst.egg-info/dependency_links.txt +0 -0
  392. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst.egg-info/entry_points.txt +0 -0
  393. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/src/pycatalyst.egg-info/top_level.txt +0 -0
  394. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/__init__.py +0 -0
  395. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/conftest.py +0 -0
  396. {pycatalyst-0.0.2/src/pycatalyst/sinks → pycatalyst-0.0.7.dev0/tests/integration}/__init__.py +0 -0
  397. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/test_version.py +0 -0
  398. {pycatalyst-0.0.2/src/pycatalyst/ui → pycatalyst-0.0.7.dev0/tests/unit}/__init__.py +0 -0
  399. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_database_sink.py +0 -0
  400. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_errors.py +0 -0
  401. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_file_sink.py +0 -0
  402. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_generators.py +0 -0
  403. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_http_sink.py +0 -0
  404. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_kafka_sink.py +0 -0
  405. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_protocols.py +0 -0
  406. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_rabbitmq_sink.py +0 -0
  407. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_recipes.py +0 -0
  408. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_schema.py +0 -0
  409. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_sinks.py +0 -0
  410. {pycatalyst-0.0.2 → pycatalyst-0.0.7.dev0}/tests/unit/test_types.py +0 -0
@@ -33,7 +33,7 @@ jobs:
33
33
  - name: Install dependencies and run tests
34
34
  run: |
35
35
  python -m pip install --upgrade pip
36
- pip install -e ".[dev,kafka,rabbitmq]"
36
+ pip install -e ".[dev]"
37
37
  pytest
38
38
 
39
39
  - name: Set up Node.js
@@ -11,10 +11,10 @@ jobs:
11
11
  fail-fast: false
12
12
  matrix:
13
13
  python-version: ['3.11', '3.12', '3.13']
14
-
14
+
15
15
  steps:
16
16
  - uses: actions/checkout@v4
17
-
17
+
18
18
  - name: Set up Python ${{ matrix.python-version }}
19
19
  uses: actions/setup-python@v5
20
20
  with:
@@ -25,11 +25,11 @@ jobs:
25
25
  - name: Install dependencies
26
26
  run: |
27
27
  python -m pip install --upgrade pip
28
- pip install -e ".[dev,kafka,rabbitmq]"
29
-
28
+ pip install -e ".[dev]"
29
+
30
30
  - name: Run tests
31
31
  run: pytest
32
-
32
+
33
33
  - name: Run linting
34
34
  run: |
35
35
  ruff format --check src/pycatalyst tests
@@ -33,3 +33,18 @@ htmlcov/
33
33
  # OS
34
34
  .DS_Store
35
35
  Thumbs.db
36
+
37
+ # Docs build (MkDocs)
38
+ site/
39
+
40
+ # Next.js / Node UI (if added)
41
+ src/pycatalyst/ui/.next/
42
+ src/pycatalyst/ui/out/
43
+ src/pycatalyst/ui/node_modules/
44
+ src/pycatalyst/ui/.env*.local
45
+ src/pycatalyst/ui/static/
46
+
47
+ # Local SQLite and temp
48
+ *.db
49
+ tmp/
50
+ *.log
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycatalyst
3
- Version: 0.0.2
3
+ Version: 0.0.7.dev0
4
4
  Summary: A schema-aware data generation and testing platform for Python
5
5
  Author-email: StatFYI <contact@statfyi.com>
6
6
  License-Expression: MIT
@@ -54,6 +54,11 @@ Requires-Dist: fastapi>=0.104.0; extra == "ui"
54
54
  Requires-Dist: uvicorn[standard]>=0.24.0; extra == "ui"
55
55
  Requires-Dist: httpx>=0.24.0; extra == "ui"
56
56
  Requires-Dist: aiofiles>=23.0.0; extra == "ui"
57
+ Provides-Extra: workbench
58
+ Requires-Dist: pycatalyst[api,sandbox]; extra == "workbench"
59
+ Requires-Dist: ipython>=8.0.0; extra == "workbench"
60
+ Provides-Extra: sandbox
61
+ Requires-Dist: docker>=7.0.0; extra == "sandbox"
57
62
  Provides-Extra: db
58
63
  Requires-Dist: alembic>=1.13.0; extra == "db"
59
64
  Requires-Dist: sqlalchemy>=2.0.0; extra == "db"
@@ -63,7 +68,7 @@ Provides-Extra: docs
63
68
  Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
64
69
  Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "docs"
65
70
  Provides-Extra: all
66
- Requires-Dist: pycatalyst[api,db,docs,faker,http,inference,parquet,postgres,ui,worker]; extra == "all"
71
+ Requires-Dist: pycatalyst[api,db,docs,faker,http,inference,parquet,postgres,sandbox,ui,workbench,worker]; extra == "all"
67
72
  Provides-Extra: dev
68
73
  Requires-Dist: pycatalyst[all]; extra == "dev"
69
74
  Requires-Dist: pytest>=7.0.0; extra == "dev"
@@ -0,0 +1,67 @@
1
+ # PyCatalyst Configuration
2
+ #
3
+ # Copy this file to pycatalyst.cfg and update with your values.
4
+ # Config keys use the same names as environment variables (e.g. PYCATALYST_DATABASE_URL).
5
+ # Environment variables override values in this file.
6
+ #
7
+ # Config file search order: current directory, ~/.pycatalyst/, package directory,
8
+ # then project root (directory containing alembic.ini).
9
+ #
10
+ # For UI dev/serve, the API URL is taken from PYCATALYST_API_URL (environment only;
11
+ # not read from this file). Default: http://localhost:8006
12
+
13
+ # =============================================================================
14
+ # [database]
15
+ # =============================================================================
16
+ # Database URL for API and db CLI (init, upgrade, seed). Required for persistence.
17
+
18
+ [database]
19
+ # PYCATALYST_DATABASE_URL = sqlite:///pycatalyst.db
20
+ PYCATALYST_DATABASE_URL = postgresql://postgres:1234567890@localhost:5432/postgres
21
+
22
+ # =============================================================================
23
+ # [auth]
24
+ # =============================================================================
25
+ # API authentication. Set PYCATALYST_AUTH_DISABLED = 1 to disable (all routes open).
26
+
27
+ [auth]
28
+ # PYCATALYST_AUTH_DISABLED = 0
29
+ PYCATALYST_AUTH_INITIAL_USERNAME = admin
30
+ PYCATALYST_AUTH_INITIAL_PASSWORD = changeme
31
+ PYCATALYST_AUTH_JWT_SECRET = your-secret-change-in-production
32
+ # Optional: external auth service (token introspect)
33
+ # PYCATALYST_AUTH_SERVICE_URL = https://auth.example.com
34
+ # PYCATALYST_AUTH_SERVICE_INTROSPECT_PATH = /introspect
35
+
36
+ # =============================================================================
37
+ # [ui]
38
+ # =============================================================================
39
+ # Global UI theme and app name when the UI is served (pycatalyst ui serve / ui dev).
40
+ # Used by the API app-config endpoint and by the static UI when config is injected.
41
+
42
+ [ui]
43
+ # Theme: light, dark, system, or named palettes (ocean, forest, sunset,
44
+ # high-contrast, sepia, violet, cathay).
45
+ PYCATALYST_UI_THEME = violet
46
+
47
+ # Website / app name shown in the UI (default: PyCatalyst).
48
+ PYCATALYST_UI_APP_NAME = PyCatalyst
49
+
50
+ # =============================================================================
51
+ # [workbench]
52
+ # =============================================================================
53
+ # Web terminal workbench for interactive Python development and library testing.
54
+
55
+ [workbench]
56
+ # Max concurrent terminal sessions per environment (default: 10)
57
+ # PYCATALYST_WORKBENCH_MAX_SESSIONS = 10
58
+ # Default shell command (default: auto-detect bash/zsh/sh)
59
+ # PYCATALYST_WORKBENCH_SHELL = /bin/bash
60
+ # Directory for venv environments (default: ~/.pycatalyst/workbench/)
61
+ # PYCATALYST_WORKBENCH_VENV_DIR = ~/.pycatalyst/workbench
62
+ # Max environments (default: 5)
63
+ # PYCATALYST_WORKBENCH_MAX_ENVIRONMENTS = 5
64
+ # Docker image for container environments (default: python:3.13-slim)
65
+ # PYCATALYST_WORKBENCH_DOCKER_IMAGE = python:3.13-slim
66
+ # Docker container memory limit (default: 512m)
67
+ # PYCATALYST_WORKBENCH_DOCKER_MEM_LIMIT = 512m
@@ -0,0 +1,67 @@
1
+ # PyCatalyst Configuration
2
+ #
3
+ # Copy this file to pycatalyst.cfg and update with your values.
4
+ # Config keys use the same names as environment variables (e.g. PYCATALYST_DATABASE_URL).
5
+ # Environment variables override values in this file.
6
+ #
7
+ # Config file search order: current directory, ~/.pycatalyst/, package directory,
8
+ # then project root (directory containing alembic.ini).
9
+ #
10
+ # For UI dev/serve, the API URL is taken from PYCATALYST_API_URL (environment only;
11
+ # not read from this file). Default: http://localhost:8006
12
+
13
+ # =============================================================================
14
+ # [database]
15
+ # =============================================================================
16
+ # Database URL for API and db CLI (init, upgrade, seed). Required for persistence.
17
+
18
+ [database]
19
+ # PYCATALYST_DATABASE_URL = sqlite:///pycatalyst.db
20
+ # PYCATALYST_DATABASE_URL = postgresql://user:password@localhost:5432/pycatalyst
21
+
22
+ # =============================================================================
23
+ # [auth]
24
+ # =============================================================================
25
+ # API authentication. Set PYCATALYST_AUTH_DISABLED = 1 to disable (all routes open).
26
+
27
+ [auth]
28
+ # PYCATALYST_AUTH_DISABLED = 0
29
+ # PYCATALYST_AUTH_INITIAL_USERNAME = admin
30
+ # PYCATALYST_AUTH_INITIAL_PASSWORD = changeme
31
+ # PYCATALYST_AUTH_JWT_SECRET = your-secret-change-in-production
32
+ # Optional: external auth service (token introspect)
33
+ # PYCATALYST_AUTH_SERVICE_URL = https://auth.example.com
34
+ # PYCATALYST_AUTH_SERVICE_INTROSPECT_PATH = /introspect
35
+
36
+ # =============================================================================
37
+ # [ui]
38
+ # =============================================================================
39
+ # Global UI theme and app name when the UI is served (pycatalyst ui serve / ui dev).
40
+ # Used by the API app-config endpoint and by the static UI when config is injected.
41
+
42
+ [ui]
43
+ # Theme: light, dark, system, or named palettes (ocean, forest, sunset,
44
+ # high-contrast, sepia, violet, cathay).
45
+ # PYCATALYST_UI_THEME = light
46
+
47
+ # Website / app name shown in the UI (default: PyCatalyst).
48
+ # PYCATALYST_UI_APP_NAME = PyCatalyst
49
+
50
+ # =============================================================================
51
+ # [workbench]
52
+ # =============================================================================
53
+ # Web terminal workbench for interactive Python development and library testing.
54
+
55
+ [workbench]
56
+ # Max concurrent terminal sessions per environment (default: 10)
57
+ # PYCATALYST_WORKBENCH_MAX_SESSIONS = 10
58
+ # Default shell command (default: auto-detect bash/zsh/sh)
59
+ # PYCATALYST_WORKBENCH_SHELL = /bin/bash
60
+ # Directory for venv environments (default: ~/.pycatalyst/workbench/)
61
+ # PYCATALYST_WORKBENCH_VENV_DIR = ~/.pycatalyst/workbench
62
+ # Max environments (default: 5)
63
+ # PYCATALYST_WORKBENCH_MAX_ENVIRONMENTS = 5
64
+ # Docker image for container environments (default: python:3.13-slim)
65
+ # PYCATALYST_WORKBENCH_DOCKER_IMAGE = python:3.13-slim
66
+ # Docker container memory limit (default: 512m)
67
+ # PYCATALYST_WORKBENCH_DOCKER_MEM_LIMIT = 512m
@@ -109,6 +109,17 @@ ui = [
109
109
  "aiofiles>=23.0.0",
110
110
  ]
111
111
 
112
+ # --- Workbench (web terminal for interactive testing) ---
113
+ workbench = [
114
+ "pycatalyst[api,sandbox]",
115
+ "ipython>=8.0.0",
116
+ ]
117
+
118
+ # --- Sandbox (Docker container isolation for workbench) ---
119
+ sandbox = [
120
+ "docker>=7.0.0",
121
+ ]
122
+
112
123
  # --- Package-specific extras ---
113
124
  db = [
114
125
  "alembic>=1.13.0",
@@ -125,7 +136,7 @@ docs = [
125
136
 
126
137
  # --- Aggregate extras ---
127
138
  all = [
128
- "pycatalyst[inference,faker,parquet,http,api,worker,ui,db,postgres,docs]",
139
+ "pycatalyst[inference,faker,parquet,http,api,worker,ui,workbench,sandbox,db,postgres,docs]",
129
140
  ]
130
141
  dev = [
131
142
  "pycatalyst[all]",
@@ -158,6 +169,7 @@ pycatalyst = [
158
169
  "db/migrations/README",
159
170
  # Recipe templates and seed data
160
171
  "data/templates/recipes/*.yaml",
172
+ "data/seed/.gitkeep",
161
173
  "data/seed/*.yaml",
162
174
  # UI static files (build with: pycatalyst ui build)
163
175
  "ui/static/*",
File without changes
@@ -0,0 +1,8 @@
1
+ """API dependencies (auth, database)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pycatalyst.api.dependencies.auth import get_current_user
6
+ from pycatalyst.api.dependencies.database import get_db_session
7
+
8
+ __all__ = ["get_current_user", "get_db_session"]
@@ -0,0 +1,143 @@
1
+ """
2
+ Authentication dependency for API routes.
3
+
4
+ Mirrors pystator: initial credentials from env or pycatalyst.cfg; JWT issued and verified in-process.
5
+ Optional auth service: token introspect via HTTP. When auth is disabled, get_current_user returns anonymous.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import hmac
11
+ import time
12
+ from typing import Any
13
+
14
+ import jwt
15
+ from fastapi import Depends, HTTPException, status
16
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
17
+
18
+ from pycatalyst.config.auth import (
19
+ get_auth_initial_credentials,
20
+ get_auth_jwt_secret,
21
+ get_auth_service_introspect_path,
22
+ get_auth_service_url,
23
+ is_auth_disabled,
24
+ )
25
+
26
+ security = HTTPBearer(auto_error=False)
27
+
28
+ ACCESS_TOKEN_EXPIRE_SECONDS = 3600 # 1 hour
29
+
30
+
31
+ def _constant_time_compare(a: str, b: str) -> bool:
32
+ """Constant-time string comparison to avoid timing attacks."""
33
+ return hmac.compare_digest(a.encode("utf-8"), b.encode("utf-8"))
34
+
35
+
36
+ def verify_initial_credentials(username: str, password: str) -> bool:
37
+ """Verify username/password against initial credentials. Returns True if valid."""
38
+ creds = get_auth_initial_credentials()
39
+ if not creds:
40
+ return False
41
+ u, p = creds
42
+ return _constant_time_compare(username, u) and _constant_time_compare(password, p)
43
+
44
+
45
+ def create_access_token(
46
+ username: str, expires_delta_seconds: int = ACCESS_TOKEN_EXPIRE_SECONDS
47
+ ) -> str:
48
+ """Create a JWT access token for the given username."""
49
+ secret = get_auth_jwt_secret()
50
+ if not secret:
51
+ raise ValueError(
52
+ "JWT secret not configured (set PYCATALYST_AUTH_JWT_SECRET in env or pycatalyst.cfg [auth])"
53
+ )
54
+ payload = {
55
+ "sub": username,
56
+ "exp": int(time.time()) + expires_delta_seconds,
57
+ "iat": int(time.time()),
58
+ }
59
+ return jwt.encode(payload, secret, algorithm="HS256")
60
+
61
+
62
+ def verify_jwt(token: str) -> dict[str, Any] | None:
63
+ """Verify JWT and return payload (with 'sub' = username) or None if invalid."""
64
+ secret = get_auth_jwt_secret()
65
+ if not secret:
66
+ return None
67
+ try:
68
+ payload = jwt.decode(token, secret, algorithms=["HS256"])
69
+ return payload
70
+ except jwt.PyJWTError:
71
+ return None
72
+
73
+
74
+ async def introspect_token(token: str) -> dict[str, Any] | None:
75
+ """Call auth service introspect endpoint. Return payload if active, else None."""
76
+ base = get_auth_service_url()
77
+ path = get_auth_service_introspect_path()
78
+ if not base or not path:
79
+ return None
80
+ url = f"{base.rstrip('/')}{path}"
81
+ try:
82
+ import httpx
83
+
84
+ async with httpx.AsyncClient() as client:
85
+ r = await client.post(
86
+ url,
87
+ data={"token": token},
88
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
89
+ timeout=5.0,
90
+ )
91
+ if r.status_code != 200:
92
+ return None
93
+ data = r.json()
94
+ if not data.get("active", False):
95
+ return None
96
+ return data
97
+ except Exception:
98
+ return None
99
+
100
+
101
+ async def get_current_user(
102
+ credentials: HTTPAuthorizationCredentials | None = Depends(security),
103
+ ) -> dict[str, Any]:
104
+ """
105
+ Resolve current user from Bearer token. Uses JWT verify or auth service introspect.
106
+ When auth is disabled, returns a dummy user so routes do not need to branch.
107
+ """
108
+ if is_auth_disabled():
109
+ return {"username": "anonymous", "auth_disabled": True}
110
+
111
+ token = None
112
+ if credentials and credentials.credentials:
113
+ token = credentials.credentials
114
+
115
+ if not token:
116
+ raise HTTPException(
117
+ status_code=status.HTTP_401_UNAUTHORIZED,
118
+ detail="Not authenticated",
119
+ headers={"WWW-Authenticate": "Bearer"},
120
+ )
121
+
122
+ payload = verify_jwt(token)
123
+ if payload is None and (
124
+ get_auth_service_url() and get_auth_service_introspect_path()
125
+ ):
126
+ payload = await introspect_token(token)
127
+
128
+ if payload is None:
129
+ raise HTTPException(
130
+ status_code=status.HTTP_401_UNAUTHORIZED,
131
+ detail="Invalid or expired token",
132
+ headers={"WWW-Authenticate": "Bearer"},
133
+ )
134
+
135
+ username = payload.get("sub") or payload.get("username")
136
+ if not username:
137
+ raise HTTPException(
138
+ status_code=status.HTTP_401_UNAUTHORIZED,
139
+ detail="Invalid token payload",
140
+ headers={"WWW-Authenticate": "Bearer"},
141
+ )
142
+
143
+ return {"username": username}
@@ -0,0 +1,131 @@
1
+ """Database session dependency for API routes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from collections.abc import Generator
7
+ from pathlib import Path
8
+
9
+ from fastapi import HTTPException, status
10
+ from sqlalchemy import create_engine, inspect, text
11
+ from sqlalchemy.orm import Session
12
+
13
+ from pycatalyst.config import set_database_url
14
+ from pycatalyst.db.base import Base, get_session
15
+ from pycatalyst.db.config import get_db_url, get_migrations_dir, is_default_db_url
16
+
17
+
18
+ def _ensure_sqlite_initialized(db_url: str) -> None:
19
+ """
20
+ Ensure SQLite database is initialized with all tables.
21
+ Auto-initializes SQLite if it doesn't exist or is uninitialized.
22
+ """
23
+ if not db_url.startswith("sqlite://"):
24
+ return
25
+ import logging
26
+
27
+ logger = logging.getLogger(__name__)
28
+ try:
29
+ db_path = db_url[10:] if db_url.startswith("sqlite:///") else db_url
30
+ if db_path == ":memory:":
31
+ return
32
+ Path(db_path).parent.mkdir(parents=True, exist_ok=True)
33
+ engine = create_engine(db_url)
34
+ inspector = inspect(engine)
35
+ if "app_metadata" not in inspector.get_table_names():
36
+ logger.info("Auto-initializing SQLite database: %s", db_url)
37
+
38
+ for table in Base.metadata.tables.values():
39
+ if table.schema == "pycatalyst":
40
+ table.schema = None
41
+ Base.metadata.create_all(engine)
42
+ try:
43
+ from alembic import command
44
+ from alembic.config import Config
45
+
46
+ versions_dir = get_migrations_dir() / "versions"
47
+ if versions_dir.exists() and any(versions_dir.iterdir()):
48
+ set_database_url(db_url)
49
+ cfg = Config()
50
+ cfg.set_main_option("script_location", str(get_migrations_dir()))
51
+ cfg.set_main_option("sqlalchemy.url", db_url)
52
+ command.upgrade(cfg, "head")
53
+ logger.info("SQLite database initialized with migrations")
54
+ else:
55
+ logger.info("SQLite database initialized with base tables")
56
+ except Exception:
57
+ logger.info("SQLite database initialized with base tables")
58
+ except Exception as e:
59
+ logger.warning("Could not auto-initialize SQLite database: %s", e)
60
+
61
+
62
+ def get_db_session() -> Generator[Session, None, None]:
63
+ """
64
+ FastAPI dependency to get database session.
65
+
66
+ Defaults to SQLite (sqlite:///pycatalyst.db) if no database URL is configured.
67
+ Automatically initializes SQLite database if it doesn't exist or is uninitialized.
68
+ """
69
+ import logging
70
+
71
+ logger = logging.getLogger(__name__)
72
+ db_url = get_db_url(required=False)
73
+ if is_default_db_url(db_url):
74
+ logger.warning(
75
+ "No database URL configured. Using default SQLite: %s\n"
76
+ "To use PostgreSQL, set PYCATALYST_DATABASE_URL:\n"
77
+ " export PYCATALYST_DATABASE_URL='postgresql://user:password@localhost:5432/pycatalyst'",
78
+ db_url,
79
+ )
80
+ else:
81
+ masked_url = db_url
82
+ if "@" in db_url and "://" in db_url:
83
+ parts = db_url.split("@", 1)
84
+ if ":" in parts[0]:
85
+ user_pass = parts[0].split("://", 1)[1]
86
+ if ":" in user_pass:
87
+ user, _ = user_pass.split(":", 1)
88
+ masked_url = (
89
+ db_url.split(":", 2)[0] + "://" + user + ":****@" + parts[1]
90
+ )
91
+ logger.info("Using database: %s", masked_url)
92
+ _ensure_sqlite_initialized(db_url)
93
+ session = None
94
+ try:
95
+ session = get_session(db_url)
96
+ session.execute(text("SELECT 1"))
97
+ yield session
98
+ except Exception as e:
99
+ if session:
100
+ try:
101
+ session.rollback()
102
+ except Exception:
103
+ pass
104
+ logger.error("Database session error: %s", e, exc_info=True)
105
+ error_detail = "Failed to connect to database"
106
+ error_msg = str(e).lower()
107
+ if "no such table" in error_msg or (
108
+ "table" in error_msg and "doesn't exist" in error_msg
109
+ ):
110
+ error_detail = "Database tables not found. Run: pycatalyst db init"
111
+ elif "database is locked" in error_msg:
112
+ error_detail = (
113
+ "Database is locked. Close other connections or wait and try again."
114
+ )
115
+ elif "permission denied" in error_msg or "access denied" in error_msg:
116
+ error_detail = "Database permission denied. Check file permissions."
117
+ else:
118
+ if os.getenv("ENVIRONMENT") == "development" or not os.getenv(
119
+ "ENVIRONMENT"
120
+ ):
121
+ error_detail = f"Failed to connect to database: {e}"
122
+ raise HTTPException(
123
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
124
+ detail=error_detail,
125
+ )
126
+ finally:
127
+ if session:
128
+ try:
129
+ session.close()
130
+ except Exception:
131
+ pass
@@ -12,13 +12,14 @@ import os
12
12
  from collections.abc import AsyncGenerator
13
13
  from contextlib import asynccontextmanager
14
14
 
15
- from fastapi import FastAPI, HTTPException, Request, status
15
+ from fastapi import Depends, FastAPI, HTTPException, Request, status
16
16
  from fastapi.exceptions import RequestValidationError
17
17
  from fastapi.middleware.cors import CORSMiddleware
18
18
  from fastapi.responses import JSONResponse
19
19
 
20
20
  from pycatalyst import __version__ as catalyst_version
21
- from pycatalyst.api.routes.v1 import generate, infer, recipes
21
+ from pycatalyst.api.dependencies.auth import get_current_user
22
+ from pycatalyst.api.routes.v1 import auth, generate, infer, recipes, settings, workbench
22
23
 
23
24
  API_VERSION = "v1"
24
25
  API_PREFIX = f"/api/{API_VERSION}"
@@ -27,7 +28,14 @@ API_PREFIX = f"/api/{API_VERSION}"
27
28
  @asynccontextmanager
28
29
  async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
29
30
  """Lifespan context manager for FastAPI application."""
31
+ import asyncio
32
+
33
+ from pycatalyst.services.workbench_manager import workbench_manager
34
+
35
+ reaper_task = asyncio.create_task(workbench_manager.run_reaper())
30
36
  yield
37
+ reaper_task.cancel()
38
+ workbench_manager.shutdown()
31
39
 
32
40
 
33
41
  def create_application() -> FastAPI:
@@ -104,14 +112,65 @@ def create_application() -> FastAPI:
104
112
  },
105
113
  )
106
114
 
107
- # Routes
108
- app.include_router(generate.router, prefix=API_PREFIX, tags=["generate"])
109
- app.include_router(infer.router, prefix=API_PREFIX, tags=["infer"])
110
- app.include_router(recipes.router, prefix=API_PREFIX, tags=["recipes"])
115
+ # Auth router (login, logout, me)
116
+ app.include_router(auth.router, prefix=API_PREFIX, tags=["Auth"])
117
+
118
+ # Public settings (app-config for UI; no auth)
119
+ app.include_router(
120
+ settings.public_router,
121
+ prefix=API_PREFIX,
122
+ tags=["Settings"],
123
+ )
124
+
125
+ # Protected routes: require valid Bearer when auth enabled
126
+ auth_dep = [Depends(get_current_user)]
127
+ app.include_router(
128
+ generate.router,
129
+ prefix=API_PREFIX,
130
+ tags=["generate"],
131
+ dependencies=auth_dep,
132
+ )
133
+ app.include_router(
134
+ infer.router,
135
+ prefix=API_PREFIX,
136
+ tags=["infer"],
137
+ dependencies=auth_dep,
138
+ )
139
+ app.include_router(
140
+ recipes.router,
141
+ prefix=API_PREFIX,
142
+ tags=["recipes"],
143
+ dependencies=auth_dep,
144
+ )
145
+
146
+ # Workbench public (capabilities — no auth)
147
+ app.include_router(
148
+ workbench.public_router,
149
+ prefix=API_PREFIX,
150
+ tags=["workbench"],
151
+ )
152
+ # Workbench REST (environment/session CRUD) — protected by HTTPBearer auth
153
+ app.include_router(
154
+ workbench.router,
155
+ prefix=API_PREFIX,
156
+ tags=["workbench"],
157
+ dependencies=auth_dep,
158
+ )
159
+ # Workbench WebSocket — auth via token query param (HTTPBearer can't work over WS)
160
+ app.include_router(
161
+ workbench.ws_router,
162
+ prefix=API_PREFIX,
163
+ tags=["workbench"],
164
+ )
111
165
 
112
166
  # Health check
113
167
  @app.get("/health")
114
168
  async def health() -> dict[str, str]:
115
- return {"status": "ok", "version": catalyst_version}
169
+ out: dict[str, str] = {"status": "ok", "version": catalyst_version}
170
+ try:
171
+ out["workbench"] = workbench.workbench_manager.workbench_health()
172
+ except Exception:
173
+ out["workbench"] = "unavailable"
174
+ return out
116
175
 
117
176
  return app
@@ -0,0 +1,8 @@
1
+ """Request and response models for PyCatalyst API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pycatalyst.api.models.requests import LoginRequest
6
+ from pycatalyst.api.models.responses import LoginResponse, UserResponse
7
+
8
+ __all__ = ["LoginRequest", "LoginResponse", "UserResponse"]
@@ -0,0 +1,12 @@
1
+ """Request models for PyCatalyst API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class LoginRequest(BaseModel):
9
+ """Request body for authentication."""
10
+
11
+ username: str = Field(..., description="Username")
12
+ password: str = Field(..., description="Password")