@cloudgrid-io/mcp 0.13.0 → 0.15.0

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 (364) hide show
  1. package/package.json +1 -1
  2. package/src/corpus/capability-map.md +81 -7
  3. package/src/corpus/templates/_cloudgrid.yaml.reference +228 -0
  4. package/src/corpus/templates/admin-dashboard/cloudgrid.yaml +226 -8
  5. package/src/corpus/templates/admin-dashboard/index.md +3 -0
  6. package/src/corpus/templates/ai-app/cloudgrid.yaml +225 -11
  7. package/src/corpus/templates/ai-app/index.md +3 -11
  8. package/src/corpus/templates/ai-knowledge-base/AGENTS.md +254 -0
  9. package/src/corpus/templates/ai-knowledge-base/README.md +28 -0
  10. package/src/corpus/templates/ai-knowledge-base/cloudgrid.yaml +238 -0
  11. package/src/corpus/templates/ai-knowledge-base/index.md +81 -0
  12. package/src/corpus/templates/analytics-dashboard/README.md +85 -0
  13. package/src/corpus/templates/analytics-dashboard/cloudgrid.yaml +232 -0
  14. package/src/corpus/templates/analytics-dashboard/index.md +316 -0
  15. package/src/corpus/templates/analytics-dashboard/services/web/app/api/events/route.js +60 -0
  16. package/src/corpus/templates/analytics-dashboard/services/web/app/event-form.js +100 -0
  17. package/src/corpus/templates/analytics-dashboard/services/web/app/layout.js +41 -0
  18. package/src/corpus/templates/analytics-dashboard/services/web/app/page.js +54 -0
  19. package/src/corpus/templates/analytics-dashboard/services/web/lib/db.js +32 -0
  20. package/src/corpus/templates/analytics-dashboard/services/web/package.json +16 -0
  21. package/src/corpus/templates/api-dashboard/README.md +84 -0
  22. package/src/corpus/templates/api-dashboard/cloudgrid.yaml +233 -0
  23. package/src/corpus/templates/api-dashboard/index.md +310 -0
  24. package/src/corpus/templates/api-dashboard/services/web/app/api/requests/route.js +62 -0
  25. package/src/corpus/templates/api-dashboard/services/web/app/dashboard.js +156 -0
  26. package/src/corpus/templates/api-dashboard/services/web/app/layout.js +42 -0
  27. package/src/corpus/templates/api-dashboard/services/web/app/page.js +39 -0
  28. package/src/corpus/templates/api-dashboard/services/web/lib/db.js +32 -0
  29. package/src/corpus/templates/api-dashboard/services/web/package.json +16 -0
  30. package/src/corpus/templates/api-docs/cloudgrid.yaml +222 -4
  31. package/src/corpus/templates/api-docs/index.md +3 -5
  32. package/src/corpus/templates/api-service/cloudgrid.yaml +224 -9
  33. package/src/corpus/templates/api-service/index.md +3 -11
  34. package/src/corpus/templates/app-with-data/cloudgrid.yaml +226 -8
  35. package/src/corpus/templates/app-with-data/index.md +3 -8
  36. package/src/corpus/templates/appointment-booking/AGENTS.md +223 -0
  37. package/src/corpus/templates/appointment-booking/README.md +16 -0
  38. package/src/corpus/templates/appointment-booking/cloudgrid.yaml +250 -0
  39. package/src/corpus/templates/appointment-booking/index.md +72 -0
  40. package/src/corpus/templates/approval-workflow/AGENTS.md +170 -0
  41. package/src/corpus/templates/approval-workflow/README.md +12 -0
  42. package/src/corpus/templates/approval-workflow/cloudgrid.yaml +237 -0
  43. package/src/corpus/templates/approval-workflow/index.md +58 -0
  44. package/src/corpus/templates/billing-dashboard/AGENTS.md +199 -0
  45. package/src/corpus/templates/billing-dashboard/README.md +18 -0
  46. package/src/corpus/templates/billing-dashboard/cloudgrid.yaml +235 -0
  47. package/src/corpus/templates/billing-dashboard/index.md +87 -0
  48. package/src/corpus/templates/blog-cms/README.md +94 -0
  49. package/src/corpus/templates/blog-cms/cloudgrid.yaml +233 -0
  50. package/src/corpus/templates/blog-cms/index.md +358 -0
  51. package/src/corpus/templates/blog-cms/services/web/app/admin/admin-editor.js +133 -0
  52. package/src/corpus/templates/blog-cms/services/web/app/admin/page.js +33 -0
  53. package/src/corpus/templates/blog-cms/services/web/app/api/posts/route.js +84 -0
  54. package/src/corpus/templates/blog-cms/services/web/app/layout.js +47 -0
  55. package/src/corpus/templates/blog-cms/services/web/app/page.js +56 -0
  56. package/src/corpus/templates/blog-cms/services/web/app/posts/[slug]/page.js +45 -0
  57. package/src/corpus/templates/blog-cms/services/web/lib/db.js +32 -0
  58. package/src/corpus/templates/blog-cms/services/web/package.json +16 -0
  59. package/src/corpus/templates/booking-system/AGENTS.md +189 -0
  60. package/src/corpus/templates/booking-system/README.md +17 -0
  61. package/src/corpus/templates/booking-system/cloudgrid.yaml +248 -0
  62. package/src/corpus/templates/booking-system/index.md +86 -0
  63. package/src/corpus/templates/calendar-scheduler/AGENTS.md +226 -0
  64. package/src/corpus/templates/calendar-scheduler/README.md +25 -0
  65. package/src/corpus/templates/calendar-scheduler/cloudgrid.yaml +259 -0
  66. package/src/corpus/templates/calendar-scheduler/index.md +85 -0
  67. package/src/corpus/templates/changelog/cloudgrid.yaml +222 -4
  68. package/src/corpus/templates/changelog/index.md +3 -5
  69. package/src/corpus/templates/company-website/README.md +54 -0
  70. package/src/corpus/templates/company-website/cloudgrid.yaml +233 -0
  71. package/src/corpus/templates/company-website/index.html +309 -0
  72. package/src/corpus/templates/company-website/index.md +29 -0
  73. package/src/corpus/templates/course-platform/AGENTS.md +161 -0
  74. package/src/corpus/templates/course-platform/README.md +26 -0
  75. package/src/corpus/templates/course-platform/cloudgrid.yaml +237 -0
  76. package/src/corpus/templates/course-platform/index.md +70 -0
  77. package/src/corpus/templates/crm/cloudgrid.yaml +226 -8
  78. package/src/corpus/templates/crm/index.md +3 -0
  79. package/src/corpus/templates/dashboard/cloudgrid.yaml +228 -4
  80. package/src/corpus/templates/dashboard/index.md +3 -5
  81. package/src/corpus/templates/deck/cloudgrid.yaml +228 -4
  82. package/src/corpus/templates/deck/index.md +3 -5
  83. package/src/corpus/templates/directory/README.md +75 -0
  84. package/src/corpus/templates/directory/cloudgrid.yaml +233 -0
  85. package/src/corpus/templates/directory/index.md +334 -0
  86. package/src/corpus/templates/directory/services/web/app/api/entries/route.js +63 -0
  87. package/src/corpus/templates/directory/services/web/app/directory-list.js +146 -0
  88. package/src/corpus/templates/directory/services/web/app/layout.js +44 -0
  89. package/src/corpus/templates/directory/services/web/app/page.js +38 -0
  90. package/src/corpus/templates/directory/services/web/lib/db.js +32 -0
  91. package/src/corpus/templates/directory/services/web/package.json +16 -0
  92. package/src/corpus/templates/docs-site/cloudgrid.yaml +222 -4
  93. package/src/corpus/templates/docs-site/index.md +3 -5
  94. package/src/corpus/templates/erp/AGENTS.md +116 -0
  95. package/src/corpus/templates/erp/README.md +17 -0
  96. package/src/corpus/templates/erp/cloudgrid.yaml +235 -0
  97. package/src/corpus/templates/erp/index.md +81 -0
  98. package/src/corpus/templates/event-board/README.md +74 -0
  99. package/src/corpus/templates/event-board/cloudgrid.yaml +233 -0
  100. package/src/corpus/templates/event-board/index.md +299 -0
  101. package/src/corpus/templates/event-board/services/web/app/api/events/route.js +61 -0
  102. package/src/corpus/templates/event-board/services/web/app/event-form.js +118 -0
  103. package/src/corpus/templates/event-board/services/web/app/layout.js +39 -0
  104. package/src/corpus/templates/event-board/services/web/app/page.js +34 -0
  105. package/src/corpus/templates/event-board/services/web/lib/db.js +32 -0
  106. package/src/corpus/templates/event-board/services/web/package.json +16 -0
  107. package/src/corpus/templates/expense-tracker/README.md +76 -0
  108. package/src/corpus/templates/expense-tracker/cloudgrid.yaml +233 -0
  109. package/src/corpus/templates/expense-tracker/index.md +305 -0
  110. package/src/corpus/templates/expense-tracker/services/web/app/api/expenses/route.js +61 -0
  111. package/src/corpus/templates/expense-tracker/services/web/app/expense-form.js +145 -0
  112. package/src/corpus/templates/expense-tracker/services/web/app/layout.js +42 -0
  113. package/src/corpus/templates/expense-tracker/services/web/app/page.js +34 -0
  114. package/src/corpus/templates/expense-tracker/services/web/lib/db.js +32 -0
  115. package/src/corpus/templates/expense-tracker/services/web/package.json +16 -0
  116. package/src/corpus/templates/feature-request-board/README.md +87 -0
  117. package/src/corpus/templates/feature-request-board/cloudgrid.yaml +233 -0
  118. package/src/corpus/templates/feature-request-board/index.md +317 -0
  119. package/src/corpus/templates/feature-request-board/services/web/app/api/requests/route.js +92 -0
  120. package/src/corpus/templates/feature-request-board/services/web/app/board.js +91 -0
  121. package/src/corpus/templates/feature-request-board/services/web/app/layout.js +43 -0
  122. package/src/corpus/templates/feature-request-board/services/web/app/page.js +35 -0
  123. package/src/corpus/templates/feature-request-board/services/web/lib/db.js +32 -0
  124. package/src/corpus/templates/feature-request-board/services/web/package.json +16 -0
  125. package/src/corpus/templates/financial-dashboard/README.md +80 -0
  126. package/src/corpus/templates/financial-dashboard/cloudgrid.yaml +233 -0
  127. package/src/corpus/templates/financial-dashboard/index.md +326 -0
  128. package/src/corpus/templates/financial-dashboard/services/web/app/api/entries/route.js +63 -0
  129. package/src/corpus/templates/financial-dashboard/services/web/app/entry-board.js +130 -0
  130. package/src/corpus/templates/financial-dashboard/services/web/app/layout.js +46 -0
  131. package/src/corpus/templates/financial-dashboard/services/web/app/page.js +31 -0
  132. package/src/corpus/templates/financial-dashboard/services/web/lib/db.js +32 -0
  133. package/src/corpus/templates/financial-dashboard/services/web/package.json +16 -0
  134. package/src/corpus/templates/forum/AGENTS.md +196 -0
  135. package/src/corpus/templates/forum/README.md +12 -0
  136. package/src/corpus/templates/forum/cloudgrid.yaml +237 -0
  137. package/src/corpus/templates/forum/index.md +66 -0
  138. package/src/corpus/templates/hr-portal/AGENTS.md +174 -0
  139. package/src/corpus/templates/hr-portal/README.md +14 -0
  140. package/src/corpus/templates/hr-portal/cloudgrid.yaml +237 -0
  141. package/src/corpus/templates/hr-portal/index.md +56 -0
  142. package/src/corpus/templates/internal-tools-portal/AGENTS.md +201 -0
  143. package/src/corpus/templates/internal-tools-portal/README.md +17 -0
  144. package/src/corpus/templates/internal-tools-portal/cloudgrid.yaml +237 -0
  145. package/src/corpus/templates/internal-tools-portal/index.md +62 -0
  146. package/src/corpus/templates/inventory/README.md +71 -0
  147. package/src/corpus/templates/inventory/cloudgrid.yaml +233 -0
  148. package/src/corpus/templates/inventory/index.md +68 -0
  149. package/src/corpus/templates/inventory/services/web/app/api/items/route.js +88 -0
  150. package/src/corpus/templates/inventory/services/web/app/inventory-manager.js +101 -0
  151. package/src/corpus/templates/inventory/services/web/app/layout.js +36 -0
  152. package/src/corpus/templates/inventory/services/web/app/page.js +37 -0
  153. package/src/corpus/templates/inventory/services/web/lib/db.js +32 -0
  154. package/src/corpus/templates/inventory/services/web/package.json +16 -0
  155. package/src/corpus/templates/invoice/README.md +71 -0
  156. package/src/corpus/templates/invoice/cloudgrid.yaml +233 -0
  157. package/src/corpus/templates/invoice/index.md +68 -0
  158. package/src/corpus/templates/invoice/services/web/app/api/invoices/route.js +82 -0
  159. package/src/corpus/templates/invoice/services/web/app/invoice-manager.js +101 -0
  160. package/src/corpus/templates/invoice/services/web/app/layout.js +35 -0
  161. package/src/corpus/templates/invoice/services/web/app/page.js +34 -0
  162. package/src/corpus/templates/invoice/services/web/lib/db.js +32 -0
  163. package/src/corpus/templates/invoice/services/web/package.json +16 -0
  164. package/src/corpus/templates/job-board/README.md +71 -0
  165. package/src/corpus/templates/job-board/cloudgrid.yaml +233 -0
  166. package/src/corpus/templates/job-board/index.md +68 -0
  167. package/src/corpus/templates/job-board/services/web/app/api/jobs/route.js +66 -0
  168. package/src/corpus/templates/job-board/services/web/app/job-board.js +73 -0
  169. package/src/corpus/templates/job-board/services/web/app/layout.js +35 -0
  170. package/src/corpus/templates/job-board/services/web/app/page.js +32 -0
  171. package/src/corpus/templates/job-board/services/web/lib/db.js +32 -0
  172. package/src/corpus/templates/job-board/services/web/package.json +16 -0
  173. package/src/corpus/templates/kanban/cloudgrid.yaml +226 -8
  174. package/src/corpus/templates/kanban/index.md +3 -0
  175. package/src/corpus/templates/landing-page/cloudgrid.yaml +228 -4
  176. package/src/corpus/templates/landing-page/index.md +3 -5
  177. package/src/corpus/templates/lms/AGENTS.md +193 -0
  178. package/src/corpus/templates/lms/README.md +27 -0
  179. package/src/corpus/templates/lms/cloudgrid.yaml +237 -0
  180. package/src/corpus/templates/lms/index.md +79 -0
  181. package/src/corpus/templates/marketplace/AGENTS.md +208 -0
  182. package/src/corpus/templates/marketplace/README.md +22 -0
  183. package/src/corpus/templates/marketplace/cloudgrid.yaml +233 -0
  184. package/src/corpus/templates/marketplace/index.md +76 -0
  185. package/src/corpus/templates/membership-site/AGENTS.md +160 -0
  186. package/src/corpus/templates/membership-site/README.md +18 -0
  187. package/src/corpus/templates/membership-site/cloudgrid.yaml +236 -0
  188. package/src/corpus/templates/membership-site/index.md +66 -0
  189. package/src/corpus/templates/monitoring-dashboard/README.md +84 -0
  190. package/src/corpus/templates/monitoring-dashboard/cloudgrid.yaml +233 -0
  191. package/src/corpus/templates/monitoring-dashboard/index.md +328 -0
  192. package/src/corpus/templates/monitoring-dashboard/services/web/app/api/checks/route.js +76 -0
  193. package/src/corpus/templates/monitoring-dashboard/services/web/app/check-form.js +124 -0
  194. package/src/corpus/templates/monitoring-dashboard/services/web/app/layout.js +44 -0
  195. package/src/corpus/templates/monitoring-dashboard/services/web/app/page.js +49 -0
  196. package/src/corpus/templates/monitoring-dashboard/services/web/lib/db.js +32 -0
  197. package/src/corpus/templates/monitoring-dashboard/services/web/package.json +16 -0
  198. package/src/corpus/templates/online-store/AGENTS.md +153 -0
  199. package/src/corpus/templates/online-store/README.md +16 -0
  200. package/src/corpus/templates/online-store/cloudgrid.yaml +234 -0
  201. package/src/corpus/templates/online-store/index.md +81 -0
  202. package/src/corpus/templates/portfolio/cloudgrid.yaml +222 -4
  203. package/src/corpus/templates/portfolio/index.md +3 -5
  204. package/src/corpus/templates/product-catalog/README.md +74 -0
  205. package/src/corpus/templates/product-catalog/cloudgrid.yaml +232 -0
  206. package/src/corpus/templates/product-catalog/index.md +330 -0
  207. package/src/corpus/templates/product-catalog/services/web/app/api/products/route.js +69 -0
  208. package/src/corpus/templates/product-catalog/services/web/app/catalog.js +153 -0
  209. package/src/corpus/templates/product-catalog/services/web/app/layout.js +49 -0
  210. package/src/corpus/templates/product-catalog/services/web/app/page.js +36 -0
  211. package/src/corpus/templates/product-catalog/services/web/lib/db.js +32 -0
  212. package/src/corpus/templates/product-catalog/services/web/package.json +16 -0
  213. package/src/corpus/templates/product-launch/README.md +46 -0
  214. package/src/corpus/templates/product-launch/cloudgrid.yaml +234 -0
  215. package/src/corpus/templates/product-launch/index.html +320 -0
  216. package/src/corpus/templates/product-launch/index.md +34 -0
  217. package/src/corpus/templates/project-management/README.md +83 -0
  218. package/src/corpus/templates/project-management/cloudgrid.yaml +233 -0
  219. package/src/corpus/templates/project-management/index.md +346 -0
  220. package/src/corpus/templates/project-management/services/web/app/api/projects/route.js +48 -0
  221. package/src/corpus/templates/project-management/services/web/app/api/tasks/route.js +85 -0
  222. package/src/corpus/templates/project-management/services/web/app/board.js +182 -0
  223. package/src/corpus/templates/project-management/services/web/app/layout.js +45 -0
  224. package/src/corpus/templates/project-management/services/web/app/page.js +49 -0
  225. package/src/corpus/templates/project-management/services/web/lib/db.js +32 -0
  226. package/src/corpus/templates/project-management/services/web/package.json +16 -0
  227. package/src/corpus/templates/property-listings/README.md +76 -0
  228. package/src/corpus/templates/property-listings/cloudgrid.yaml +233 -0
  229. package/src/corpus/templates/property-listings/index.md +335 -0
  230. package/src/corpus/templates/property-listings/services/web/app/api/properties/route.js +73 -0
  231. package/src/corpus/templates/property-listings/services/web/app/layout.js +45 -0
  232. package/src/corpus/templates/property-listings/services/web/app/listings.js +152 -0
  233. package/src/corpus/templates/property-listings/services/web/app/page.js +53 -0
  234. package/src/corpus/templates/property-listings/services/web/lib/db.js +32 -0
  235. package/src/corpus/templates/property-listings/services/web/package.json +16 -0
  236. package/src/corpus/templates/quiz-platform/README.md +84 -0
  237. package/src/corpus/templates/quiz-platform/cloudgrid.yaml +233 -0
  238. package/src/corpus/templates/quiz-platform/index.md +313 -0
  239. package/src/corpus/templates/quiz-platform/services/web/app/api/questions/route.js +69 -0
  240. package/src/corpus/templates/quiz-platform/services/web/app/layout.js +47 -0
  241. package/src/corpus/templates/quiz-platform/services/web/app/page.js +37 -0
  242. package/src/corpus/templates/quiz-platform/services/web/app/quiz.js +188 -0
  243. package/src/corpus/templates/quiz-platform/services/web/lib/db.js +32 -0
  244. package/src/corpus/templates/quiz-platform/services/web/package.json +16 -0
  245. package/src/corpus/templates/report/cloudgrid.yaml +228 -4
  246. package/src/corpus/templates/report/index.md +3 -5
  247. package/src/corpus/templates/restaurant-reservations/AGENTS.md +258 -0
  248. package/src/corpus/templates/restaurant-reservations/README.md +17 -0
  249. package/src/corpus/templates/restaurant-reservations/cloudgrid.yaml +258 -0
  250. package/src/corpus/templates/restaurant-reservations/index.md +62 -0
  251. package/src/corpus/templates/revenue-dashboard/README.md +75 -0
  252. package/src/corpus/templates/revenue-dashboard/cloudgrid.yaml +233 -0
  253. package/src/corpus/templates/revenue-dashboard/index.md +307 -0
  254. package/src/corpus/templates/revenue-dashboard/services/web/app/api/sales/route.js +60 -0
  255. package/src/corpus/templates/revenue-dashboard/services/web/app/layout.js +40 -0
  256. package/src/corpus/templates/revenue-dashboard/services/web/app/page.js +87 -0
  257. package/src/corpus/templates/revenue-dashboard/services/web/app/sale-form.js +63 -0
  258. package/src/corpus/templates/revenue-dashboard/services/web/lib/db.js +32 -0
  259. package/src/corpus/templates/revenue-dashboard/services/web/package.json +16 -0
  260. package/src/corpus/templates/saas-marketing/cloudgrid.yaml +222 -4
  261. package/src/corpus/templates/saas-marketing/index.md +3 -5
  262. package/src/corpus/templates/semantic-search/AGENTS.md +192 -0
  263. package/src/corpus/templates/semantic-search/README.md +74 -0
  264. package/src/corpus/templates/semantic-search/cloudgrid.yaml +217 -0
  265. package/src/corpus/templates/semantic-search/index.md +97 -0
  266. package/src/corpus/templates/semantic-search/services/backend/requirements.txt +7 -0
  267. package/src/corpus/templates/semantic-search/services/backend/src/app/__init__.py +0 -0
  268. package/src/corpus/templates/semantic-search/services/backend/src/app/answer.py +90 -0
  269. package/src/corpus/templates/semantic-search/services/backend/src/app/auth.py +65 -0
  270. package/src/corpus/templates/semantic-search/services/backend/src/app/config.py +110 -0
  271. package/src/corpus/templates/semantic-search/services/backend/src/app/db.py +89 -0
  272. package/src/corpus/templates/semantic-search/services/backend/src/app/embeddings.py +44 -0
  273. package/src/corpus/templates/semantic-search/services/backend/src/app/indexing.py +222 -0
  274. package/src/corpus/templates/semantic-search/services/backend/src/app/main.py +259 -0
  275. package/src/corpus/templates/semantic-search/services/backend/src/app/search.py +222 -0
  276. package/src/corpus/templates/semantic-search/services/backend/src/app/source.py +259 -0
  277. package/src/corpus/templates/semantic-search/services/backend/src/main.py +12 -0
  278. package/src/corpus/templates/semantic-search/services/web/index.html +12 -0
  279. package/src/corpus/templates/semantic-search/services/web/package-lock.json +1680 -0
  280. package/src/corpus/templates/semantic-search/services/web/package.json +19 -0
  281. package/src/corpus/templates/semantic-search/services/web/src/App.jsx +55 -0
  282. package/src/corpus/templates/semantic-search/services/web/src/api.js +125 -0
  283. package/src/corpus/templates/semantic-search/services/web/src/components/Dashboard.jsx +61 -0
  284. package/src/corpus/templates/semantic-search/services/web/src/components/ManagerLogin.jsx +39 -0
  285. package/src/corpus/templates/semantic-search/services/web/src/components/SearchView.jsx +166 -0
  286. package/src/corpus/templates/semantic-search/services/web/src/components/Settings.jsx +148 -0
  287. package/src/corpus/templates/semantic-search/services/web/src/main.jsx +10 -0
  288. package/src/corpus/templates/semantic-search/services/web/src/styles.css +78 -0
  289. package/src/corpus/templates/semantic-search/services/web/vite.config.js +10 -0
  290. package/src/corpus/templates/status-page/cloudgrid.yaml +222 -4
  291. package/src/corpus/templates/status-page/index.md +3 -5
  292. package/src/corpus/templates/subscription-management/AGENTS.md +191 -0
  293. package/src/corpus/templates/subscription-management/README.md +21 -0
  294. package/src/corpus/templates/subscription-management/cloudgrid.yaml +236 -0
  295. package/src/corpus/templates/subscription-management/index.md +73 -0
  296. package/src/corpus/templates/task-manager/cloudgrid.yaml +226 -8
  297. package/src/corpus/templates/task-manager/index.md +3 -0
  298. package/src/corpus/templates/ticket-system/README.md +71 -0
  299. package/src/corpus/templates/ticket-system/cloudgrid.yaml +233 -0
  300. package/src/corpus/templates/ticket-system/index.md +68 -0
  301. package/src/corpus/templates/ticket-system/services/web/app/api/tickets/route.js +81 -0
  302. package/src/corpus/templates/ticket-system/services/web/app/layout.js +36 -0
  303. package/src/corpus/templates/ticket-system/services/web/app/page.js +35 -0
  304. package/src/corpus/templates/ticket-system/services/web/app/ticket-queue.js +112 -0
  305. package/src/corpus/templates/ticket-system/services/web/lib/db.js +32 -0
  306. package/src/corpus/templates/ticket-system/services/web/package.json +16 -0
  307. package/src/corpus/templates/time-tracking/README.md +81 -0
  308. package/src/corpus/templates/time-tracking/cloudgrid.yaml +233 -0
  309. package/src/corpus/templates/time-tracking/index.md +298 -0
  310. package/src/corpus/templates/time-tracking/services/web/app/api/entries/route.js +66 -0
  311. package/src/corpus/templates/time-tracking/services/web/app/entry-form.js +106 -0
  312. package/src/corpus/templates/time-tracking/services/web/app/layout.js +39 -0
  313. package/src/corpus/templates/time-tracking/services/web/app/page.js +30 -0
  314. package/src/corpus/templates/time-tracking/services/web/lib/db.js +32 -0
  315. package/src/corpus/templates/time-tracking/services/web/package.json +16 -0
  316. package/src/corpus/templates/travel-booking/AGENTS.md +207 -0
  317. package/src/corpus/templates/travel-booking/README.md +17 -0
  318. package/src/corpus/templates/travel-booking/cloudgrid.yaml +249 -0
  319. package/src/corpus/templates/travel-booking/index.md +88 -0
  320. package/src/corpus/templates/waitlist/cloudgrid.yaml +222 -4
  321. package/src/corpus/templates/waitlist/index.md +3 -5
  322. package/src/corpus/templates/web-app/cloudgrid.yaml +228 -4
  323. package/src/corpus/templates/web-app/index.md +3 -5
  324. package/src/corpus/workflows/ai-knowledge-base.md +137 -0
  325. package/src/corpus/workflows/analytics-dashboard.md +104 -0
  326. package/src/corpus/workflows/api-dashboard.md +102 -0
  327. package/src/corpus/workflows/appointment-booking.md +143 -0
  328. package/src/corpus/workflows/approval-workflow.md +124 -0
  329. package/src/corpus/workflows/billing-dashboard.md +120 -0
  330. package/src/corpus/workflows/blog-cms.md +104 -0
  331. package/src/corpus/workflows/booking-system.md +116 -0
  332. package/src/corpus/workflows/calendar-scheduler.md +134 -0
  333. package/src/corpus/workflows/company-website.md +46 -0
  334. package/src/corpus/workflows/course-platform.md +126 -0
  335. package/src/corpus/workflows/directory.md +101 -0
  336. package/src/corpus/workflows/erp.md +125 -0
  337. package/src/corpus/workflows/event-board.md +102 -0
  338. package/src/corpus/workflows/expense-tracker.md +102 -0
  339. package/src/corpus/workflows/feature-request-board.md +105 -0
  340. package/src/corpus/workflows/financial-dashboard.md +103 -0
  341. package/src/corpus/workflows/forum.md +128 -0
  342. package/src/corpus/workflows/hr-portal.md +115 -0
  343. package/src/corpus/workflows/internal-tools-portal.md +125 -0
  344. package/src/corpus/workflows/inventory.md +101 -0
  345. package/src/corpus/workflows/invoice.md +101 -0
  346. package/src/corpus/workflows/job-board.md +101 -0
  347. package/src/corpus/workflows/lms.md +113 -0
  348. package/src/corpus/workflows/marketplace.md +123 -0
  349. package/src/corpus/workflows/membership-site.md +128 -0
  350. package/src/corpus/workflows/monitoring-dashboard.md +104 -0
  351. package/src/corpus/workflows/online-store.md +114 -0
  352. package/src/corpus/workflows/product-catalog.md +105 -0
  353. package/src/corpus/workflows/product-launch.md +43 -0
  354. package/src/corpus/workflows/project-management.md +106 -0
  355. package/src/corpus/workflows/property-listings.md +101 -0
  356. package/src/corpus/workflows/quiz-platform.md +105 -0
  357. package/src/corpus/workflows/restaurant-reservations.md +123 -0
  358. package/src/corpus/workflows/revenue-dashboard.md +102 -0
  359. package/src/corpus/workflows/semantic-search.md +133 -0
  360. package/src/corpus/workflows/subscription-management.md +141 -0
  361. package/src/corpus/workflows/ticket-system.md +101 -0
  362. package/src/corpus/workflows/time-tracking.md +104 -0
  363. package/src/corpus/workflows/travel-booking.md +115 -0
  364. package/src/tools.js +73 -12
@@ -0,0 +1,217 @@
1
+ # ============================================================================
2
+ # cloudgrid.yaml — Complete Example
3
+ # ============================================================================
4
+ #
5
+ # This file shows EVERY field the platform supports, with comments explaining
6
+ # each one. It is a reference — no real project would use all of these at once.
7
+ #
8
+ # STANDARD: every CloudGrid template ships its cloudgrid.yaml in THIS full form.
9
+ # Uncomment ONLY the fields the archetype needs (always: name + services; plus
10
+ # needs: for runtime apps). Leave everything else commented. Comments are
11
+ # ignored by the parser, so the file deploys to exactly the active fields.
12
+ #
13
+ # Only two fields are required: name and services. Everything else is optional.
14
+ # Full docs: docs/cloudgrid-yaml-reference.md
15
+ # ============================================================================
16
+
17
+
18
+ # ─── IDENTITY ───────────────────────────────────────────────────────────────
19
+
20
+ # Entity slug (REQUIRED). 2-42 chars, lowercase alphanumeric + hyphens.
21
+ # Must start and end with a letter or digit.
22
+ # Becomes part of your public URL: <name>-<4hex>.<org>.cloudgrid.io
23
+ name: semantic-search
24
+
25
+ # One-line summary. Max 280 chars. If omitted, Grid Brain auto-generates one.
26
+ # description: Hybrid semantic + keyword search over your documents
27
+
28
+ # Single emoji or short text shown as the entity icon. Max 64 chars.
29
+ # icon: "🔎"
30
+
31
+ # Entity type: 'app' (default) or 'agent'. An agent is an app with an agent: block.
32
+ # kind: app
33
+
34
+
35
+ # ─── NETWORKING ─────────────────────────────────────────────────────────────
36
+
37
+ # Whether this entity serves public HTTP traffic.
38
+ # true = public URL + Ingress (default). false = internal only.
39
+ # expose: true
40
+
41
+ # Additional domain names this entity serves on (the platform URL is always active).
42
+ # custom_domains:
43
+ # - orders.example.com
44
+ # - shop.example.com
45
+
46
+
47
+ # ─── HEALTH PROBE (entity-level override) ──────────────────────────────────
48
+
49
+ # Override the default health check for the entire entity.
50
+ # probe:
51
+ # path: /healthz # HTTP path to probe (default: /).
52
+ # expected: "<500" # '<500' (default), '200', '2xx'.
53
+
54
+
55
+ # ─── DEPLOY NOTIFICATIONS ──────────────────────────────────────────────────
56
+
57
+ # Slack notification to the org's default channel on deploy failure.
58
+ # true = notify (default), false = silent.
59
+ # notify: true
60
+
61
+
62
+ # ─── SCALING ────────────────────────────────────────────────────────────────
63
+
64
+ # Replica scaling intent.
65
+ # 'auto' — platform decides from the grid tier (default).
66
+ # 'alert' — single-replica, emit a SPOF warning at plug time.
67
+ # 1-10 — explicit replica count, clamped to the grid max.
68
+ # scale: auto
69
+
70
+
71
+ # ─── INFRASTRUCTURE NEEDS ──────────────────────────────────────────────────
72
+ #
73
+ # Declare infrastructure; the platform provisions it and injects connection
74
+ # details as env vars. Nine needs: database, cache, kv, queue, pubsub, vector,
75
+ # object_storage, disk, ai. Simplest form is `true`.
76
+ #
77
+ # needs:
78
+ # # database — MongoDB. Injects DATABASE_MONGODB_URL (+ legacy MONGODB_URL).
79
+ # # true | pool | dedicated | { tier: external, secret: MY_DB }
80
+ # database: true
81
+ # # cache — Redis, LRU eviction (derived data). Injects CACHE_REDIS_URL (+ REDIS_URL).
82
+ # # true | redis | { engine: external, secret: MY_REDIS_URL }
83
+ # cache: true
84
+ # # kv — Redis, no eviction (durable: flags, idempotency). Injects KV_REDIS_URL.
85
+ # kv: true
86
+ # # queue — Redis job-queue substrate (BullMQ). Injects QUEUE_REDIS_URL.
87
+ # queue: true
88
+ # # pubsub — Redis broadcast pub/sub. Injects PUBSUB_REDIS_URL.
89
+ # pubsub: true
90
+ # # vector — pgvector on Postgres. Injects VECTOR_PGVECTOR_URL (+ PGVECTOR_URL).
91
+ # # true | pgvector | { engine: pgvector, dim: 1536 }
92
+ # # NOT USED HERE — pgvector runtime role-grant is blocked (platform #1545).
93
+ # # This template stores embeddings in the Mongo `chunks` collection and
94
+ # # cosine-ranks in-app instead. Swap to `vector: pgvector` once #1545 lands.
95
+ # vector: pgvector
96
+ # # object_storage — GCS bucket. Injects OBJECT_STORAGE_GCS_BUCKET, _GCS_REGION.
97
+ # # true | { size: 10Gi }
98
+ # object_storage: true
99
+ # # disk — persistent FS mount (PVC). Injects DISK_PATH. Pins to 1 replica.
100
+ # # true | { size: 1Gi, mount: /var/data } (forbidden: /proc /sys /dev)
101
+ # # Use with SOURCE_TYPE=local to read documents from the mount.
102
+ # disk: true
103
+ # # ai — AI Gateway via @cloudgrid-io/ai. Injects AI_GATEWAY_URL. true | false.
104
+ # ai: true
105
+
106
+
107
+ # ─── INTER-ENTITY COMMUNICATION ────────────────────────────────────────────
108
+
109
+ # Entities this app calls (outbound). Service-account token injected per pair.
110
+ # calls:
111
+ # - org:atomic/billing-api
112
+
113
+ # Inbound caller policy — who can call YOUR entity.
114
+ # callers:
115
+ # policy: auto # 'auto' | 'manual' (default).
116
+ # scope: org # 'org' (default) | 'space' | 'explicit'.
117
+ # fallback: deny # 'deny' (default) | 'warn'.
118
+ # # allowed_apps: # required only when scope: explicit
119
+ # # - org:atomic/frontend
120
+
121
+
122
+ # ─── VAULT ──────────────────────────────────────────────────────────────────
123
+
124
+ # Map org vault items to env vars. Keys UPPER_SNAKE_CASE; values = vault item keys.
125
+ # The source + embeddings secrets can also be stored directly with
126
+ # `grid secrets set KEY=VALUE` — see AGENTS.md.
127
+ # vault:
128
+ # EMBEDDINGS_API_KEY: embeddings-api-key
129
+ # DROPBOX_REFRESH_TOKEN: dropbox-refresh-token
130
+
131
+
132
+ # ─── CONNECTORS AND HOOKS ──────────────────────────────────────────────────
133
+
134
+ # connectors:
135
+ # - id: team-slack
136
+ # target: org # 'system' | 'org'.
137
+ # channel: "#deploys"
138
+ # hooks:
139
+ # - on: deploy.failed # release.published | deploy.failed | feedback.received | entity.created
140
+ # via: team-slack
141
+ # template: "Deploy failed for {entity_name}: {error}"
142
+ # channel: "#alerts"
143
+ # triggers:
144
+ # - from: team-slack
145
+ # event: message
146
+ # channel: "#support"
147
+ # route: /webhooks/slack
148
+
149
+
150
+ # ─── LOCAL DEVELOPMENT ──────────────────────────────────────────────────────
151
+
152
+ # Config for `grid dev`. Redact fields in dev data snapshots.
153
+ # dev:
154
+ # snapshot:
155
+ # mask:
156
+ # - users.email
157
+ # - users.phone
158
+
159
+
160
+ # ─── AGENT METADATA ────────────────────────────────────────────────────────
161
+
162
+ # An agent is an app with kind: agent (above) and an agent: block.
163
+ # agent:
164
+ # purpose: Sync customer data from external CRM every night
165
+ # schedule: "0 2 * * *"
166
+ # trigger: webhook
167
+
168
+
169
+ # ─── PRIVATE RESOURCE AUTO-DEPENDENCIES ────────────────────────────────────
170
+
171
+ # When true (default), private resources are auto-injected as depends_on.
172
+ # auto_depends_on_private_requires: true
173
+
174
+
175
+ # ─── SERVICES (REQUIRED) ───────────────────────────────────────────────────
176
+ #
177
+ # At least one service is required. Key = service name (slug rules).
178
+ # Types: node, nextjs, python, static, cron.
179
+ #
180
+ services:
181
+ # React (Vite) built to static assets, served at the root.
182
+ web:
183
+ type: static
184
+ path: /
185
+ build:
186
+ command: npm run build
187
+ output: dist
188
+ node_version: "22"
189
+ # Python FastAPI backend — hybrid search, answer mode, manager admin.
190
+ # Entry: services/backend/src/main.py (uvicorn app.main:app).
191
+ backend:
192
+ type: python
193
+ path: /backend
194
+ # A scheduled `type: cron` refresh service is a follow-up — a Python cron
195
+ # entity is currently blocked on platform issue #1585. Until it lands, use the
196
+ # manager "Refresh now" endpoint (POST /backend/admin/refresh). See AGENTS.md.
197
+ # refresh:
198
+ # type: cron
199
+ # schedule: "0 3 * * *"
200
+ # timezone: UTC
201
+ # run: job
202
+ # source:
203
+ # path: services/refresh
204
+ # CloudGrid managed Mongo → injects DATABASE_MONGODB_URL (+ legacy MONGODB_URL).
205
+ # NO `needs: vector` — pgvector runtime role-grant is still blocked (#1545);
206
+ # embeddings live in the Mongo `chunks` collection and are cosine-ranked in-app.
207
+ needs:
208
+ database: true
209
+
210
+
211
+ # ============================================================================
212
+ # NOTES
213
+ # - NOT here: org:/entity_id: (→ .cloudgrid/link.json), secrets (→ grid secrets set),
214
+ # Dockerfile (platform generates from services.*.type).
215
+ # - Deploy: grid plug | Local with grid resources: grid dev
216
+ # - Full reference: docs/cloudgrid-yaml-reference.md | CLI: docs/cli-reference.md
217
+ # ============================================================================
@@ -0,0 +1,97 @@
1
+ # Template: semantic-search (CloudGrid-native document search)
2
+
3
+ A deployable, domain-agnostic **document search** app — index a folder of
4
+ documents (PDF/DOCX/TXT/MD), then search by keyword, by meaning, and by metadata,
5
+ with a grounded answer mode and a manager admin. Two services: a React (Vite)
6
+ static frontend at `/` and a FastAPI backend at `/backend`, backed by
7
+ grid-shared Mongo. This template ships **real, runnable code** — read `AGENTS.md`,
8
+ adapt the naming/metadata to your domain, then deploy.
9
+
10
+ **Hybrid search** = Mongo `$text` (lexical) + in-app NumPy cosine over chunk
11
+ embeddings (semantic) + metadata filters, blended and aggregated to the document
12
+ level with a best-passage highlight.
13
+
14
+ **Key rules (proven by a live build — do not regress these):**
15
+
16
+ 1. **Every service's code lives under `services/<name>/`.** `path:` in
17
+ `cloudgrid.yaml` is the URL mount, NOT the filesystem path. The `web` service
18
+ → `services/web/`; the `backend` service → `services/backend/`.
19
+ 2. **Read the Mongo URL LAZILY** — `os.environ.get("DATABASE_MONGODB_URL") or
20
+ os.environ.get("MONGODB_URL")` inside `get_client()`, never at import time.
21
+ 3. **Health is green with NO secrets set.** No secret is read at import time;
22
+ every secret is gated inside a function. `GET /backend/health` needs only Mongo.
23
+ 4. **`@app.on_event("startup")` calls `db.ensure_indexes()`** so the chunks
24
+ `$text` index exists at boot — `$text` returns `[]` on an empty catalog rather
25
+ than raising `IndexNotFound` (a 500). The `$text` query is also guarded in
26
+ `except OperationFailure` as belt-and-suspenders.
27
+ 5. **No `needs: vector`** — pgvector runtime role-grant is blocked (#1545), so
28
+ embeddings live in the Mongo `chunks` collection (a float array) and are
29
+ cosine-ranked in-app. **No active cron** — a Python `type: cron` refresh is
30
+ blocked (#1585); ship the manager "Refresh now" endpoint and add cron later.
31
+
32
+ ## File tree
33
+
34
+ ```
35
+ cloudgrid.yaml # name + services(web static+build, backend python) + needs:{database:true}
36
+ services/web/ # React + Vite static frontend (LTR, neutral)
37
+ package.json vite.config.js index.html
38
+ src/main.jsx App.jsx api.js styles.css
39
+ src/components/SearchView.jsx ManagerLogin.jsx Settings.jsx Dashboard.jsx
40
+ services/backend/ # FastAPI backend, mounted at /backend
41
+ requirements.txt
42
+ src/main.py # uvicorn entry (reads PORT; no secret at import)
43
+ src/app/
44
+ main.py # routes + startup ensure_indexes()
45
+ config.py # lazy secret gating (SOURCE_TYPE, EMBEDDINGS_*, ...)
46
+ db.py # lazy Mongo; ensure_indexes(); collections documents+chunks
47
+ search.py # hybrid: $text + cosine + metadata, blended to doc level
48
+ indexing.py # discover -> extract -> chunk -> embed -> store (incremental)
49
+ source.py # pluggable adapter: dropbox | local | url
50
+ embeddings.py # one config point (OpenAI-compatible)
51
+ answer.py # grounded answer + citations (extractive fallback)
52
+ auth.py # manager session auth (MANAGER_PASSWORD_HASH)
53
+ ```
54
+
55
+ ## cloudgrid.yaml (active fields)
56
+
57
+ ```yaml
58
+ # On disk this file is the full-annotated reference (templates/_cloudgrid.yaml.reference)
59
+ # with EVERY field present as a comment; only the fields below are uncommented.
60
+ name: semantic-search
61
+ services:
62
+ web:
63
+ type: static
64
+ path: /
65
+ build:
66
+ command: npm run build
67
+ output: dist
68
+ node_version: "22"
69
+ backend:
70
+ type: python
71
+ path: /backend
72
+ needs:
73
+ database: true
74
+ ```
75
+
76
+ > **Capability:** the need is `database: true` — the deployer provisions Mongo and
77
+ > injects `DATABASE_MONGODB_URL` (+ legacy `MONGODB_URL`). NO `needs: vector`
78
+ > (#1545) and NO active cron (#1585). `requires:` is the deprecated v1 alias —
79
+ > don't mix it with `needs:`.
80
+
81
+ ## Configure + deploy
82
+
83
+ 1. `grid init semantic-search` — creates the entity + `.cloudgrid/link.json`.
84
+ 2. `grid secrets set EMBEDDINGS_API_KEY=sk-…` plus the source secrets
85
+ (`SOURCE_TYPE` + Dropbox/local/url config) and `MANAGER_PASSWORD_HASH`
86
+ (sha256 hex of the manager password). See `AGENTS.md`.
87
+ 3. (Optional) `grid dev` to run locally against the injected dev Mongo.
88
+ 4. `grid plug` to deploy — a runtime deploy is **async**: poll status until live,
89
+ then sign in as manager and hit "Refresh now" to index the source.
90
+
91
+ ## Adapt it
92
+
93
+ - Rename the `collection` / `date` metadata to your domain in
94
+ `services/backend/src/app/indexing.py` (`_parse_metadata`).
95
+ - Add a source adapter: subclass `Source` in `source.py`, return it from
96
+ `get_source()`, gate its secrets in `config.py`.
97
+ - Point embeddings at any OpenAI-compatible provider via `EMBEDDINGS_BASE_URL`.
@@ -0,0 +1,7 @@
1
+ fastapi==0.111.0
2
+ uvicorn[standard]==0.30.1
3
+ pymongo==4.8.0
4
+ numpy==1.26.4
5
+ httpx==0.27.0
6
+ pypdf==4.2.0
7
+ python-docx==1.1.2
@@ -0,0 +1,90 @@
1
+ """Grounded answer mode.
2
+
3
+ Composes an answer from the indexed documents ONLY, with citations back to the
4
+ documents used. No outside knowledge. Toggleable from Settings. If the answer LLM
5
+ is not configured, it falls back to an extractive answer (the top passages
6
+ themselves), still grounded and cited — never crashes.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import httpx
11
+
12
+ from . import config, search
13
+
14
+
15
+ def _answer_enabled_from_db() -> bool:
16
+ from . import db
17
+
18
+ doc = db.settings().find_one({"_id": "features"}) or {}
19
+ return bool(doc.get("answer_mode_enabled", True))
20
+
21
+
22
+ def compose_answer(question: str) -> dict:
23
+ """Return a grounded answer + the documents cited. Extractive fallback if no LLM."""
24
+ if not _answer_enabled_from_db():
25
+ return {"answer": None, "citations": [], "note": "Answer mode is disabled."}
26
+
27
+ hits = search.search(question, mode="hybrid", limit=5)
28
+ passages = hits["results"]
29
+ if not passages:
30
+ return {
31
+ "answer": None,
32
+ "citations": [],
33
+ "note": "No indexed material matches this question.",
34
+ }
35
+
36
+ citations = [
37
+ {"document_id": p["document_id"], "title": p["title"], "collection": p["collection"]}
38
+ for p in passages
39
+ ]
40
+
41
+ # Grounded generation only if the LLM key is present; otherwise return an
42
+ # extractive answer built from the retrieved passages.
43
+ key = config.embeddings_api_key()
44
+ if not key:
45
+ extractive = "\n\n".join(
46
+ f"- {p['title']}: {p['best_passage']}" for p in passages[:3]
47
+ )
48
+ return {
49
+ "answer": extractive,
50
+ "citations": citations,
51
+ "mode": "extractive",
52
+ "note": "Grounded generation unavailable (no API key) — showing "
53
+ "the most relevant source passages instead.",
54
+ }
55
+
56
+ context = "\n\n".join(
57
+ f"[{i+1}] {p['title']}:\n{p['best_passage']}" for i, p in enumerate(passages)
58
+ )
59
+ system = (
60
+ "You answer strictly and only from the provided document passages. "
61
+ "Cite sources as [n]. If the passages do not contain the answer, say so. "
62
+ "Never use outside knowledge."
63
+ )
64
+ try:
65
+ resp = httpx.post(
66
+ f"{config.embeddings_base_url()}/chat/completions",
67
+ headers={"Authorization": f"Bearer {key}"},
68
+ json={
69
+ "model": config.answer_model(),
70
+ "messages": [
71
+ {"role": "system", "content": system},
72
+ {"role": "user", "content": f"Question: {question}\n\nSources:\n{context}"},
73
+ ],
74
+ "temperature": 0.2,
75
+ },
76
+ timeout=60.0,
77
+ )
78
+ resp.raise_for_status()
79
+ text = resp.json()["choices"][0]["message"]["content"]
80
+ return {"answer": text, "citations": citations, "mode": "generated", "note": None}
81
+ except Exception as exc: # noqa: BLE001 - degrade to extractive, never crash
82
+ extractive = "\n\n".join(
83
+ f"- {p['title']}: {p['best_passage']}" for p in passages[:3]
84
+ )
85
+ return {
86
+ "answer": extractive,
87
+ "citations": citations,
88
+ "mode": "extractive",
89
+ "note": f"Generation failed ({exc}); showing source passages.",
90
+ }
@@ -0,0 +1,65 @@
1
+ """Server-side manager session auth.
2
+
3
+ Admin functions are enforced on the server, not just hidden in the UI. Sessions
4
+ are opaque tokens stored in Mongo. The password is compared against
5
+ MANAGER_PASSWORD_HASH (sha256 hex). If no hash is configured, admin login is
6
+ refused (never open by default) — but the app still starts and search stays
7
+ public.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import datetime as dt
12
+ import hashlib
13
+ import secrets
14
+
15
+ from fastapi import Header, HTTPException
16
+
17
+ from . import config, db
18
+
19
+ SESSION_TTL_HOURS = 12
20
+
21
+
22
+ def _hash(password: str) -> str:
23
+ return hashlib.sha256(password.encode("utf-8")).hexdigest()
24
+
25
+
26
+ def login(password: str) -> str:
27
+ configured = config.manager_password_hash()
28
+ if not configured:
29
+ raise HTTPException(
30
+ status_code=503,
31
+ detail="Manager auth is not configured (MANAGER_PASSWORD_HASH unset).",
32
+ )
33
+ if not secrets.compare_digest(_hash(password), configured):
34
+ raise HTTPException(status_code=401, detail="Invalid password.")
35
+ token = secrets.token_urlsafe(32)
36
+ db.get_db().sessions.insert_one({
37
+ "_id": token,
38
+ "created_at": dt.datetime.now(dt.timezone.utc),
39
+ "expires_at": dt.datetime.now(dt.timezone.utc) + dt.timedelta(hours=SESSION_TTL_HOURS),
40
+ })
41
+ return token
42
+
43
+
44
+ def require_manager(authorization: str | None = Header(default=None)) -> str:
45
+ """FastAPI dependency: validate the Bearer session token."""
46
+ if not authorization or not authorization.lower().startswith("bearer "):
47
+ raise HTTPException(status_code=401, detail="Manager session required.")
48
+ token = authorization.split(" ", 1)[1].strip()
49
+ session = db.get_db().sessions.find_one({"_id": token})
50
+ if not session:
51
+ raise HTTPException(status_code=401, detail="Invalid session.")
52
+ if session["expires_at"] < dt.datetime.now(dt.timezone.utc):
53
+ db.get_db().sessions.delete_one({"_id": token})
54
+ raise HTTPException(status_code=401, detail="Session expired.")
55
+ return token
56
+
57
+
58
+ def change_password(current: str, new: str) -> str:
59
+ """Self-contained password change. Returns the NEW hash for the manager to
60
+ store as the MANAGER_PASSWORD_HASH secret (we never persist the plaintext or
61
+ the secret ourselves)."""
62
+ configured = config.manager_password_hash()
63
+ if configured and not secrets.compare_digest(_hash(current), configured):
64
+ raise HTTPException(status_code=401, detail="Current password is incorrect.")
65
+ return _hash(new)
@@ -0,0 +1,110 @@
1
+ """Central configuration and secret gating.
2
+
3
+ CRITICAL CloudGrid rule: NEVER read a secret at module import time. Every value
4
+ here is resolved lazily inside a function, so the process starts and passes
5
+ `GET /backend/health` even when NO secret is set. Search and indexing degrade
6
+ gracefully (empty results + a clear "not indexed / missing secret" note) rather
7
+ than crashing.
8
+
9
+ The Mongo connection is the ONLY hard dependency for health, and it is injected
10
+ automatically by CloudGrid (`needs: database`) as DATABASE_MONGODB_URL.
11
+
12
+ Two pluggable config points:
13
+ - SOURCE_TYPE selects the document-source adapter (dropbox | local | url).
14
+ - EMBEDDINGS_* selects the embeddings provider (OpenAI-compatible by default).
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import os
19
+
20
+
21
+ # ── Mongo (injected by the grid — the only health-critical config) ──────────
22
+
23
+ def mongo_url() -> str | None:
24
+ """Injected by CloudGrid `needs: database`. Never set by hand."""
25
+ return os.environ.get("DATABASE_MONGODB_URL") or os.environ.get("MONGODB_URL")
26
+
27
+
28
+ # ── Embeddings (one config point, gated) ─────────────────────────────────────
29
+
30
+ def embeddings_api_key() -> str | None:
31
+ return os.environ.get("EMBEDDINGS_API_KEY")
32
+
33
+
34
+ def embeddings_model() -> str:
35
+ # Changing the model changes vector dimensions and requires a full reindex.
36
+ return os.environ.get("EMBEDDINGS_MODEL", "text-embedding-3-small")
37
+
38
+
39
+ def embeddings_base_url() -> str:
40
+ # Any OpenAI-compatible endpoint. Default is OpenAI.
41
+ return os.environ.get("EMBEDDINGS_BASE_URL", "https://api.openai.com/v1")
42
+
43
+
44
+ def embeddings_configured() -> bool:
45
+ return bool(embeddings_api_key())
46
+
47
+
48
+ def answer_model() -> str:
49
+ return os.environ.get("ANSWER_MODEL", "gpt-4o-mini")
50
+
51
+
52
+ # ── Source adapter (one config point, gated) ─────────────────────────────────
53
+
54
+ def source_type() -> str:
55
+ """dropbox | local | url. Selects the document-source adapter."""
56
+ return os.environ.get("SOURCE_TYPE", "dropbox")
57
+
58
+
59
+ def dropbox_config() -> dict[str, str | None]:
60
+ return {
61
+ "app_key": os.environ.get("DROPBOX_APP_KEY"),
62
+ "app_secret": os.environ.get("DROPBOX_APP_SECRET"),
63
+ "refresh_token": os.environ.get("DROPBOX_REFRESH_TOKEN"),
64
+ "folder_path": os.environ.get("DROPBOX_FOLDER_PATH", ""),
65
+ }
66
+
67
+
68
+ def local_source_path() -> str:
69
+ """Directory the local adapter reads. Point at a `needs: disk` mount
70
+ (DISK_PATH) or a bundled folder."""
71
+ return os.environ.get("LOCAL_SOURCE_PATH", os.environ.get("DISK_PATH", ""))
72
+
73
+
74
+ def url_source_manifest() -> str | None:
75
+ """A newline- or comma-separated list of document URLs the url adapter
76
+ fetches. Set as a secret or plain env var."""
77
+ return os.environ.get("URL_SOURCE_MANIFEST")
78
+
79
+
80
+ def source_configured() -> bool:
81
+ st = source_type()
82
+ if st == "dropbox":
83
+ c = dropbox_config()
84
+ return bool(c["app_key"] and c["app_secret"] and c["refresh_token"])
85
+ if st == "local":
86
+ return bool(local_source_path())
87
+ if st == "url":
88
+ return bool(url_source_manifest())
89
+ return False
90
+
91
+
92
+ # ── Manager auth (gated) ──────────────────────────────────────────────────────
93
+
94
+ def manager_password_hash() -> str | None:
95
+ """A sha256 hex digest of the manager password. Compared server-side."""
96
+ return os.environ.get("MANAGER_PASSWORD_HASH")
97
+
98
+
99
+ def manager_auth_configured() -> bool:
100
+ return bool(manager_password_hash())
101
+
102
+
103
+ # ── Search blend weights ─────────────────────────────────────────────────────
104
+
105
+ def semantic_weight() -> float:
106
+ return float(os.environ.get("SEMANTIC_WEIGHT", "0.55"))
107
+
108
+
109
+ def keyword_weight() -> float:
110
+ return float(os.environ.get("KEYWORD_WEIGHT", "0.45"))
@@ -0,0 +1,89 @@
1
+ """Lazy MongoDB access.
2
+
3
+ The client and connection string are resolved LAZILY (inside get_client), never
4
+ at module top level — so importing this module never touches the network and
5
+ never requires the env var to exist. CloudGrid injects DATABASE_MONGODB_URL at
6
+ runtime from `needs: database`.
7
+
8
+ Collections:
9
+ documents — one row per source document (title, collection tag, date, ...).
10
+ chunks — one embeddable passage per document (content + embedding array).
11
+ settings — feature flags, synonyms, catalog metadata (last_refresh).
12
+ usage_events, index_reports, sessions — telemetry / admin.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from typing import Optional
17
+
18
+ from pymongo import ASCENDING, MongoClient, TEXT
19
+ from pymongo.database import Database
20
+
21
+ from . import config
22
+
23
+ _client: Optional[MongoClient] = None
24
+
25
+
26
+ def get_client() -> MongoClient:
27
+ global _client
28
+ if _client is None:
29
+ uri = config.mongo_url()
30
+ if not uri:
31
+ raise RuntimeError(
32
+ "DATABASE_MONGODB_URL is not set. CloudGrid injects it "
33
+ "automatically from `needs: database` — run with `grid dev` "
34
+ "locally or deploy with `grid plug`. Do not set it by hand."
35
+ )
36
+ # Short server-selection timeout so health checks fail fast, not hang.
37
+ _client = MongoClient(uri, serverSelectionTimeoutMS=3000)
38
+ return _client
39
+
40
+
41
+ def get_db() -> Database:
42
+ # Default DB name comes from the connection-string path segment the grid injects.
43
+ return get_client().get_default_database()
44
+
45
+
46
+ def ping() -> bool:
47
+ """True if Mongo is reachable. Used by /backend/health."""
48
+ get_client().admin.command("ping")
49
+ return True
50
+
51
+
52
+ def ensure_indexes() -> None:
53
+ """Create the indexes the search relies on. Safe to call repeatedly.
54
+
55
+ Called at FastAPI startup so the chunks $text index exists before any query
56
+ — without it, `$text` raises IndexNotFound (a 500) on an empty catalog.
57
+ """
58
+ db = get_db()
59
+ # Text index on chunk content for lexical / $text search.
60
+ db.chunks.create_index([("content", TEXT)], name="chunks_content_text")
61
+ db.chunks.create_index([("document_id", ASCENDING)], name="chunks_document_id")
62
+ # Metadata filter indexes on documents.
63
+ db.documents.create_index([("date", ASCENDING)], name="documents_date")
64
+ db.documents.create_index([("collection", ASCENDING)], name="documents_collection")
65
+ db.documents.create_index(
66
+ [("source_filename", ASCENDING)], name="documents_source_filename", unique=True
67
+ )
68
+
69
+
70
+ # ── Collection accessors ─────────────────────────────────────────────────────
71
+
72
+ def documents():
73
+ return get_db().documents
74
+
75
+
76
+ def chunks():
77
+ return get_db().chunks
78
+
79
+
80
+ def settings():
81
+ return get_db().settings
82
+
83
+
84
+ def events():
85
+ return get_db().usage_events
86
+
87
+
88
+ def index_reports():
89
+ return get_db().index_reports