@cachly-dev/mcp-server 0.10.48 → 0.10.49

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 (308) hide show
  1. package/.github/workflows/eas-build-android.yml +58 -0
  2. package/.github/workflows/eas-update.yml +55 -0
  3. package/README.md +5 -33
  4. package/STRATEGY.md +209 -0
  5. package/apps/travel-chaos-organizer/.env.example +49 -0
  6. package/apps/travel-chaos-organizer/DEPLOY.md +328 -0
  7. package/apps/travel-chaos-organizer/Makefile +57 -0
  8. package/apps/travel-chaos-organizer/README.md +159 -0
  9. package/apps/travel-chaos-organizer/backend/.env.example +10 -0
  10. package/apps/travel-chaos-organizer/backend/Dockerfile +25 -0
  11. package/apps/travel-chaos-organizer/backend/alembic/env.py +56 -0
  12. package/apps/travel-chaos-organizer/backend/alembic/script.py.mako +24 -0
  13. package/apps/travel-chaos-organizer/backend/alembic/versions/.gitkeep +0 -0
  14. package/apps/travel-chaos-organizer/backend/alembic/versions/0001_stripe_customer_id.py +24 -0
  15. package/apps/travel-chaos-organizer/backend/alembic/versions/0002_telegram_chat_id.py +24 -0
  16. package/apps/travel-chaos-organizer/backend/alembic.ini +39 -0
  17. package/apps/travel-chaos-organizer/backend/app/__init__.py +0 -0
  18. package/apps/travel-chaos-organizer/backend/app/auth/__init__.py +0 -0
  19. package/apps/travel-chaos-organizer/backend/app/auth/keycloak.py +81 -0
  20. package/apps/travel-chaos-organizer/backend/app/config.py +66 -0
  21. package/apps/travel-chaos-organizer/backend/app/db/__init__.py +0 -0
  22. package/apps/travel-chaos-organizer/backend/app/db/database.py +20 -0
  23. package/apps/travel-chaos-organizer/backend/app/db/migrations/001_init.sql +71 -0
  24. package/apps/travel-chaos-organizer/backend/app/db/migrations/002_telemetry_freemium.sql +31 -0
  25. package/apps/travel-chaos-organizer/backend/app/db/migrations/003_telegram_chat_id.sql +4 -0
  26. package/apps/travel-chaos-organizer/backend/app/db/migrations/004_stripe_and_email.sql +5 -0
  27. package/apps/travel-chaos-organizer/backend/app/limiter.py +4 -0
  28. package/apps/travel-chaos-organizer/backend/app/logging_config.py +25 -0
  29. package/apps/travel-chaos-organizer/backend/app/main.py +77 -0
  30. package/apps/travel-chaos-organizer/backend/app/models/__init__.py +0 -0
  31. package/apps/travel-chaos-organizer/backend/app/models/schemas.py +142 -0
  32. package/apps/travel-chaos-organizer/backend/app/routers/__init__.py +0 -0
  33. package/apps/travel-chaos-organizer/backend/app/routers/admin.py +237 -0
  34. package/apps/travel-chaos-organizer/backend/app/routers/cache.py +13 -0
  35. package/apps/travel-chaos-organizer/backend/app/routers/events.py +29 -0
  36. package/apps/travel-chaos-organizer/backend/app/routers/export.py +122 -0
  37. package/apps/travel-chaos-organizer/backend/app/routers/inbox.py +122 -0
  38. package/apps/travel-chaos-organizer/backend/app/routers/items.py +130 -0
  39. package/apps/travel-chaos-organizer/backend/app/routers/mail.py +108 -0
  40. package/apps/travel-chaos-organizer/backend/app/routers/parse.py +303 -0
  41. package/apps/travel-chaos-organizer/backend/app/routers/payments.py +179 -0
  42. package/apps/travel-chaos-organizer/backend/app/routers/trips.py +134 -0
  43. package/apps/travel-chaos-organizer/backend/app/routers/users.py +169 -0
  44. package/apps/travel-chaos-organizer/backend/app/routers/waitlist.py +37 -0
  45. package/apps/travel-chaos-organizer/backend/app/services/__init__.py +0 -0
  46. package/apps/travel-chaos-organizer/backend/app/services/cache.py +131 -0
  47. package/apps/travel-chaos-organizer/backend/app/services/drip.py +128 -0
  48. package/apps/travel-chaos-organizer/backend/app/services/email.py +61 -0
  49. package/apps/travel-chaos-organizer/backend/app/services/errors.py +44 -0
  50. package/apps/travel-chaos-organizer/backend/app/services/notifier.py +10 -0
  51. package/apps/travel-chaos-organizer/backend/app/services/ollama.py +101 -0
  52. package/apps/travel-chaos-organizer/backend/app/services/parser.py +22 -0
  53. package/apps/travel-chaos-organizer/backend/app/services/quota.py +85 -0
  54. package/apps/travel-chaos-organizer/backend/app/services/sentry.py +11 -0
  55. package/apps/travel-chaos-organizer/backend/app/services/telemetry.py +35 -0
  56. package/apps/travel-chaos-organizer/backend/pytest.ini +4 -0
  57. package/apps/travel-chaos-organizer/backend/requirements-dev.txt +6 -0
  58. package/apps/travel-chaos-organizer/backend/requirements.txt +25 -0
  59. package/apps/travel-chaos-organizer/backend/tests/__init__.py +0 -0
  60. package/apps/travel-chaos-organizer/backend/tests/conftest.py +223 -0
  61. package/apps/travel-chaos-organizer/backend/tests/test_admin.py +99 -0
  62. package/apps/travel-chaos-organizer/backend/tests/test_cache.py +142 -0
  63. package/apps/travel-chaos-organizer/backend/tests/test_drip.py +32 -0
  64. package/apps/travel-chaos-organizer/backend/tests/test_e2e_chain.py +121 -0
  65. package/apps/travel-chaos-organizer/backend/tests/test_export.py +48 -0
  66. package/apps/travel-chaos-organizer/backend/tests/test_inbox.py +107 -0
  67. package/apps/travel-chaos-organizer/backend/tests/test_items.py +138 -0
  68. package/apps/travel-chaos-organizer/backend/tests/test_mail.py +113 -0
  69. package/apps/travel-chaos-organizer/backend/tests/test_ollama.py +145 -0
  70. package/apps/travel-chaos-organizer/backend/tests/test_parse_url.py +98 -0
  71. package/apps/travel-chaos-organizer/backend/tests/test_parser.py +72 -0
  72. package/apps/travel-chaos-organizer/backend/tests/test_payments.py +163 -0
  73. package/apps/travel-chaos-organizer/backend/tests/test_quota.py +46 -0
  74. package/apps/travel-chaos-organizer/backend/tests/test_search.py +67 -0
  75. package/apps/travel-chaos-organizer/backend/tests/test_trips.py +123 -0
  76. package/apps/travel-chaos-organizer/backend/tests/test_users.py +52 -0
  77. package/apps/travel-chaos-organizer/backups/.gitkeep +0 -0
  78. package/apps/travel-chaos-organizer/caddy/Caddyfile +27 -0
  79. package/apps/travel-chaos-organizer/docker-compose.node1.yml +31 -0
  80. package/apps/travel-chaos-organizer/docker-compose.override.yml +14 -0
  81. package/apps/travel-chaos-organizer/docker-compose.yml +128 -0
  82. package/apps/travel-chaos-organizer/frontend/.env.example +13 -0
  83. package/apps/travel-chaos-organizer/frontend/app/(app)/_layout.tsx +80 -0
  84. package/apps/travel-chaos-organizer/frontend/app/(app)/inbox.tsx +166 -0
  85. package/apps/travel-chaos-organizer/frontend/app/(app)/search.tsx +130 -0
  86. package/apps/travel-chaos-organizer/frontend/app/(app)/settings.tsx +381 -0
  87. package/apps/travel-chaos-organizer/frontend/app/(app)/trips/[id].tsx +346 -0
  88. package/apps/travel-chaos-organizer/frontend/app/(app)/trips.tsx +363 -0
  89. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/cancel.tsx +34 -0
  90. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/success.tsx +35 -0
  91. package/apps/travel-chaos-organizer/frontend/app/(auth)/login.tsx +91 -0
  92. package/apps/travel-chaos-organizer/frontend/app/_layout.tsx +71 -0
  93. package/apps/travel-chaos-organizer/frontend/app/auth.tsx +29 -0
  94. package/apps/travel-chaos-organizer/frontend/app/index.tsx +16 -0
  95. package/apps/travel-chaos-organizer/frontend/app/onboarding.tsx +119 -0
  96. package/apps/travel-chaos-organizer/frontend/app.config.ts +54 -0
  97. package/apps/travel-chaos-organizer/frontend/assets/adaptive-icon.svg +66 -0
  98. package/apps/travel-chaos-organizer/frontend/assets/icon.svg +79 -0
  99. package/apps/travel-chaos-organizer/frontend/assets/splash.svg +15 -0
  100. package/apps/travel-chaos-organizer/frontend/components/BottomSheet.tsx +102 -0
  101. package/apps/travel-chaos-organizer/frontend/components/ConfettiCannon.tsx +107 -0
  102. package/apps/travel-chaos-organizer/frontend/components/ConfirmDialog.tsx +109 -0
  103. package/apps/travel-chaos-organizer/frontend/components/ErrorBoundary.tsx +45 -0
  104. package/apps/travel-chaos-organizer/frontend/components/FileUploadButton.tsx +19 -0
  105. package/apps/travel-chaos-organizer/frontend/components/ImportSheet.tsx +155 -0
  106. package/apps/travel-chaos-organizer/frontend/components/OfflineBadge.tsx +55 -0
  107. package/apps/travel-chaos-organizer/frontend/components/QRCodeViewer.tsx +80 -0
  108. package/apps/travel-chaos-organizer/frontend/components/ScoreRing.tsx +39 -0
  109. package/apps/travel-chaos-organizer/frontend/components/Screen.tsx +39 -0
  110. package/apps/travel-chaos-organizer/frontend/components/Skeleton.tsx +70 -0
  111. package/apps/travel-chaos-organizer/frontend/components/SwipeableInboxItem.tsx +121 -0
  112. package/apps/travel-chaos-organizer/frontend/components/SwipeableTimelineItem.tsx +70 -0
  113. package/apps/travel-chaos-organizer/frontend/components/TimelineItem.tsx +73 -0
  114. package/apps/travel-chaos-organizer/frontend/components/TimelineItemDetail.tsx +130 -0
  115. package/apps/travel-chaos-organizer/frontend/components/Toast.tsx +60 -0
  116. package/apps/travel-chaos-organizer/frontend/components/ToastContext.tsx +42 -0
  117. package/apps/travel-chaos-organizer/frontend/components/TripCard.tsx +146 -0
  118. package/apps/travel-chaos-organizer/frontend/components/TripQRModal.tsx +70 -0
  119. package/apps/travel-chaos-organizer/frontend/components/UpgradeBanner.tsx +44 -0
  120. package/apps/travel-chaos-organizer/frontend/eas.json +22 -0
  121. package/apps/travel-chaos-organizer/frontend/hooks/useInbox.ts +24 -0
  122. package/apps/travel-chaos-organizer/frontend/hooks/useNetworkSync.ts +60 -0
  123. package/apps/travel-chaos-organizer/frontend/hooks/useTrips.ts +112 -0
  124. package/apps/travel-chaos-organizer/frontend/lib/api.ts +223 -0
  125. package/apps/travel-chaos-organizer/frontend/lib/auth.ts +93 -0
  126. package/apps/travel-chaos-organizer/frontend/lib/db.ts +86 -0
  127. package/apps/travel-chaos-organizer/frontend/lib/fileCache.ts +54 -0
  128. package/apps/travel-chaos-organizer/frontend/lib/haptics.ts +13 -0
  129. package/apps/travel-chaos-organizer/frontend/lib/notifications.ts +112 -0
  130. package/apps/travel-chaos-organizer/frontend/lib/offlineQueue.ts +145 -0
  131. package/apps/travel-chaos-organizer/frontend/lib/quota.tsx +43 -0
  132. package/apps/travel-chaos-organizer/frontend/lib/sentry.ts +28 -0
  133. package/apps/travel-chaos-organizer/frontend/lib/shareIntent.ts +44 -0
  134. package/apps/travel-chaos-organizer/frontend/package-lock.json +13037 -0
  135. package/apps/travel-chaos-organizer/frontend/package.json +44 -0
  136. package/apps/travel-chaos-organizer/frontend/tsconfig.json +23 -0
  137. package/apps/travel-chaos-organizer/keycloak/realm-export.json +59 -0
  138. package/apps/travel-chaos-organizer/landing/index.html +773 -0
  139. package/apps/travel-chaos-organizer/scripts/backup.sh +14 -0
  140. package/apps/travel-chaos-organizer/scripts/configure-env.sh +82 -0
  141. package/apps/travel-chaos-organizer/scripts/deploy.sh +54 -0
  142. package/apps/travel-chaos-organizer/scripts/health-check.sh +36 -0
  143. package/apps/travel-chaos-organizer/scripts/migrate.sh +7 -0
  144. package/apps/travel-chaos-organizer/scripts/server-setup.sh +85 -0
  145. package/apps/travel-chaos-organizer/scripts/smoke-test.sh +29 -0
  146. package/apps/travel-chaos-organizer/scripts/verify-tracking.sh +71 -0
  147. package/apps/travel-chaos-organizer/telegram-bot/.env.example +3 -0
  148. package/apps/travel-chaos-organizer/telegram-bot/Dockerfile +6 -0
  149. package/apps/travel-chaos-organizer/telegram-bot/bot.py +504 -0
  150. package/apps/travel-chaos-organizer/telegram-bot/requirements.txt +3 -0
  151. package/dist/packages/telegram-notify/client.d.ts +19 -0
  152. package/dist/packages/telegram-notify/client.js +75 -0
  153. package/dist/packages/telegram-notify/client.js.map +1 -0
  154. package/dist/src/__tests__/auth.test.js.map +1 -0
  155. package/dist/{__tests__ → src/__tests__}/brain-flow.test.js +141 -0
  156. package/dist/src/__tests__/brain-flow.test.js.map +1 -0
  157. package/dist/src/__tests__/confidence.test.js.map +1 -0
  158. package/dist/src/__tests__/e2e/api-auth.test.js.map +1 -0
  159. package/dist/src/__tests__/e2e/api-health.test.js.map +1 -0
  160. package/dist/src/__tests__/e2e/cli.test.js.map +1 -0
  161. package/dist/src/__tests__/e2e/config.js.map +1 -0
  162. package/dist/src/__tests__/e2e/funnel.test.js.map +1 -0
  163. package/dist/src/__tests__/e2e/mcp-tools.test.js.map +1 -0
  164. package/dist/src/__tests__/e2e/provisioning.test.js.map +1 -0
  165. package/dist/{__tests__ → src/__tests__}/handlers-unit.test.js +0 -2
  166. package/dist/src/__tests__/handlers-unit.test.js.map +1 -0
  167. package/dist/src/__tests__/namespace.test.js.map +1 -0
  168. package/dist/src/auth.js.map +1 -0
  169. package/dist/src/ckg.js.map +1 -0
  170. package/dist/src/confidence.js.map +1 -0
  171. package/dist/src/embeddings.js.map +1 -0
  172. package/dist/src/handlers/advanced.js.map +1 -0
  173. package/dist/{handlers → src/handlers}/brain.js +134 -30
  174. package/dist/src/handlers/brain.js.map +1 -0
  175. package/dist/src/handlers/cache.js.map +1 -0
  176. package/dist/{handlers → src/handlers}/context.js +9 -4
  177. package/dist/src/handlers/context.js.map +1 -0
  178. package/dist/{handlers → src/handlers}/fedbrain.d.ts +6 -0
  179. package/dist/{handlers → src/handlers}/fedbrain.js +19 -7
  180. package/dist/src/handlers/fedbrain.js.map +1 -0
  181. package/dist/src/handlers/instances.js.map +1 -0
  182. package/dist/{handlers → src/handlers}/roadmap.js +7 -3
  183. package/dist/src/handlers/roadmap.js.map +1 -0
  184. package/dist/src/handlers/syndicate.js.map +1 -0
  185. package/dist/src/handlers/tco.d.ts +16 -0
  186. package/dist/src/handlers/tco.js +303 -0
  187. package/dist/src/handlers/tco.js.map +1 -0
  188. package/dist/src/handlers/team.js.map +1 -0
  189. package/dist/{index.js → src/index.js} +153 -218
  190. package/dist/src/index.js.map +1 -0
  191. package/dist/src/namespace.js.map +1 -0
  192. package/dist/src/notifier.d.ts +5 -0
  193. package/dist/src/notifier.js +6 -0
  194. package/dist/src/notifier.js.map +1 -0
  195. package/dist/src/search.js.map +1 -0
  196. package/dist/src/search.test.js.map +1 -0
  197. package/dist/{tools.d.ts → src/tools.d.ts} +196 -0
  198. package/dist/{tools.js → src/tools.js} +178 -0
  199. package/dist/src/tools.js.map +1 -0
  200. package/dist/src/utils.js.map +1 -0
  201. package/package.json +3 -3
  202. package/packages/telegram-notify/SPLIT_PLAN.md +106 -0
  203. package/packages/telegram-notify/client.ts +88 -0
  204. package/packages/telegram-notify/emojis.json +22 -0
  205. package/packages/telegram-notify/pyproject.toml +17 -0
  206. package/packages/telegram-notify/telegram_notify/__init__.py +3 -0
  207. package/packages/telegram-notify/telegram_notify/client.py +87 -0
  208. package/packages/telegram-notify/telegram_notify/emojis.json +22 -0
  209. package/scripts/generate-icons.js +28 -0
  210. package/smithery.yaml +21 -8
  211. package/src/__tests__/brain-flow.test.ts +174 -0
  212. package/src/__tests__/handlers-unit.test.ts +1 -2
  213. package/src/handlers/brain.ts +138 -31
  214. package/src/handlers/context.ts +9 -4
  215. package/src/handlers/fedbrain.ts +21 -7
  216. package/src/handlers/roadmap.ts +7 -3
  217. package/src/handlers/tco.ts +347 -0
  218. package/src/index.ts +139 -220
  219. package/src/notifier.ts +5 -0
  220. package/src/tools.ts +191 -0
  221. package/tsconfig.json +2 -3
  222. package/dist/__tests__/auth.test.js.map +0 -1
  223. package/dist/__tests__/brain-flow.test.js.map +0 -1
  224. package/dist/__tests__/brain-predict-failures.test.d.ts +0 -17
  225. package/dist/__tests__/brain-predict-failures.test.js +0 -339
  226. package/dist/__tests__/brain-predict-failures.test.js.map +0 -1
  227. package/dist/__tests__/confidence.test.js.map +0 -1
  228. package/dist/__tests__/e2e/api-auth.test.js.map +0 -1
  229. package/dist/__tests__/e2e/api-health.test.js.map +0 -1
  230. package/dist/__tests__/e2e/cli.test.js.map +0 -1
  231. package/dist/__tests__/e2e/config.js.map +0 -1
  232. package/dist/__tests__/e2e/funnel.test.js.map +0 -1
  233. package/dist/__tests__/e2e/mcp-tools.test.js.map +0 -1
  234. package/dist/__tests__/e2e/provisioning.test.js.map +0 -1
  235. package/dist/__tests__/handlers-unit.test.js.map +0 -1
  236. package/dist/__tests__/namespace.test.js.map +0 -1
  237. package/dist/auth.js.map +0 -1
  238. package/dist/ckg.js.map +0 -1
  239. package/dist/confidence.js.map +0 -1
  240. package/dist/embeddings.js.map +0 -1
  241. package/dist/handlers/advanced.js.map +0 -1
  242. package/dist/handlers/brain.js.map +0 -1
  243. package/dist/handlers/cache.js.map +0 -1
  244. package/dist/handlers/context.js.map +0 -1
  245. package/dist/handlers/fedbrain.js.map +0 -1
  246. package/dist/handlers/instances.js.map +0 -1
  247. package/dist/handlers/roadmap.js.map +0 -1
  248. package/dist/handlers/syndicate.js.map +0 -1
  249. package/dist/handlers/team.js.map +0 -1
  250. package/dist/index.js.map +0 -1
  251. package/dist/namespace.js.map +0 -1
  252. package/dist/search.js.map +0 -1
  253. package/dist/search.test.js.map +0 -1
  254. package/dist/tools.js.map +0 -1
  255. package/dist/utils.js.map +0 -1
  256. package/src/__tests__/brain-predict-failures.test.ts +0 -430
  257. /package/dist/{__tests__ → src/__tests__}/auth.test.d.ts +0 -0
  258. /package/dist/{__tests__ → src/__tests__}/auth.test.js +0 -0
  259. /package/dist/{__tests__ → src/__tests__}/brain-flow.test.d.ts +0 -0
  260. /package/dist/{__tests__ → src/__tests__}/confidence.test.d.ts +0 -0
  261. /package/dist/{__tests__ → src/__tests__}/confidence.test.js +0 -0
  262. /package/dist/{__tests__ → src/__tests__}/e2e/api-auth.test.d.ts +0 -0
  263. /package/dist/{__tests__ → src/__tests__}/e2e/api-auth.test.js +0 -0
  264. /package/dist/{__tests__ → src/__tests__}/e2e/api-health.test.d.ts +0 -0
  265. /package/dist/{__tests__ → src/__tests__}/e2e/api-health.test.js +0 -0
  266. /package/dist/{__tests__ → src/__tests__}/e2e/cli.test.d.ts +0 -0
  267. /package/dist/{__tests__ → src/__tests__}/e2e/cli.test.js +0 -0
  268. /package/dist/{__tests__ → src/__tests__}/e2e/config.d.ts +0 -0
  269. /package/dist/{__tests__ → src/__tests__}/e2e/config.js +0 -0
  270. /package/dist/{__tests__ → src/__tests__}/e2e/funnel.test.d.ts +0 -0
  271. /package/dist/{__tests__ → src/__tests__}/e2e/funnel.test.js +0 -0
  272. /package/dist/{__tests__ → src/__tests__}/e2e/mcp-tools.test.d.ts +0 -0
  273. /package/dist/{__tests__ → src/__tests__}/e2e/mcp-tools.test.js +0 -0
  274. /package/dist/{__tests__ → src/__tests__}/e2e/provisioning.test.d.ts +0 -0
  275. /package/dist/{__tests__ → src/__tests__}/e2e/provisioning.test.js +0 -0
  276. /package/dist/{__tests__ → src/__tests__}/handlers-unit.test.d.ts +0 -0
  277. /package/dist/{__tests__ → src/__tests__}/namespace.test.d.ts +0 -0
  278. /package/dist/{__tests__ → src/__tests__}/namespace.test.js +0 -0
  279. /package/dist/{auth.d.ts → src/auth.d.ts} +0 -0
  280. /package/dist/{auth.js → src/auth.js} +0 -0
  281. /package/dist/{ckg.d.ts → src/ckg.d.ts} +0 -0
  282. /package/dist/{ckg.js → src/ckg.js} +0 -0
  283. /package/dist/{confidence.d.ts → src/confidence.d.ts} +0 -0
  284. /package/dist/{confidence.js → src/confidence.js} +0 -0
  285. /package/dist/{embeddings.d.ts → src/embeddings.d.ts} +0 -0
  286. /package/dist/{embeddings.js → src/embeddings.js} +0 -0
  287. /package/dist/{handlers → src/handlers}/advanced.d.ts +0 -0
  288. /package/dist/{handlers → src/handlers}/advanced.js +0 -0
  289. /package/dist/{handlers → src/handlers}/brain.d.ts +0 -0
  290. /package/dist/{handlers → src/handlers}/cache.d.ts +0 -0
  291. /package/dist/{handlers → src/handlers}/cache.js +0 -0
  292. /package/dist/{handlers → src/handlers}/context.d.ts +0 -0
  293. /package/dist/{handlers → src/handlers}/instances.d.ts +0 -0
  294. /package/dist/{handlers → src/handlers}/instances.js +0 -0
  295. /package/dist/{handlers → src/handlers}/roadmap.d.ts +0 -0
  296. /package/dist/{handlers → src/handlers}/syndicate.d.ts +0 -0
  297. /package/dist/{handlers → src/handlers}/syndicate.js +0 -0
  298. /package/dist/{handlers → src/handlers}/team.d.ts +0 -0
  299. /package/dist/{handlers → src/handlers}/team.js +0 -0
  300. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
  301. /package/dist/{namespace.d.ts → src/namespace.d.ts} +0 -0
  302. /package/dist/{namespace.js → src/namespace.js} +0 -0
  303. /package/dist/{search.d.ts → src/search.d.ts} +0 -0
  304. /package/dist/{search.js → src/search.js} +0 -0
  305. /package/dist/{search.test.d.ts → src/search.test.d.ts} +0 -0
  306. /package/dist/{search.test.js → src/search.test.js} +0 -0
  307. /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
  308. /package/dist/{utils.js → src/utils.js} +0 -0
@@ -0,0 +1,24 @@
1
+ """add telegram_chat_id to users
2
+
3
+ Revision ID: 0002
4
+ Revises: 0001
5
+ Create Date: 2025-01-02 00:00:00.000000
6
+ """
7
+ from typing import Sequence, Union
8
+ from alembic import op
9
+ import sqlalchemy as sa
10
+
11
+ revision: str = '0002'
12
+ down_revision: Union[str, None] = '0001'
13
+ branch_labels: Union[str, Sequence[str], None] = None
14
+ depends_on: Union[str, Sequence[str], None] = None
15
+
16
+
17
+ def upgrade() -> None:
18
+ op.add_column('users', sa.Column('telegram_chat_id', sa.Text(), nullable=True))
19
+ op.create_index('ix_users_telegram_chat_id', 'users', ['telegram_chat_id'], unique=True)
20
+
21
+
22
+ def downgrade() -> None:
23
+ op.drop_index('ix_users_telegram_chat_id', 'users')
24
+ op.drop_column('users', 'telegram_chat_id')
@@ -0,0 +1,39 @@
1
+ [alembic]
2
+ script_location = alembic
3
+ prepend_sys_path = .
4
+ version_path_separator = os
5
+ sqlalchemy.url = postgresql+asyncpg://tco:tco@localhost:5432/tco
6
+
7
+ [loggers]
8
+ keys = root,sqlalchemy,alembic
9
+
10
+ [handlers]
11
+ keys = console
12
+
13
+ [formatters]
14
+ keys = generic
15
+
16
+ [logger_root]
17
+ level = WARN
18
+ handlers = console
19
+ qualname =
20
+
21
+ [logger_sqlalchemy]
22
+ level = WARN
23
+ handlers =
24
+ qualname = sqlalchemy.engine
25
+
26
+ [logger_alembic]
27
+ level = INFO
28
+ handlers =
29
+ qualname = alembic
30
+
31
+ [handler_console]
32
+ class = StreamHandler
33
+ args = (sys.stderr,)
34
+ level = NOTSET
35
+ formatter = generic
36
+
37
+ [formatter_generic]
38
+ format = %(levelname)-5.5s [%(name)s] %(message)s
39
+ datefmt = %H:%M:%S
@@ -0,0 +1,81 @@
1
+ from datetime import datetime, timedelta, timezone
2
+ from typing import Annotated
3
+ import httpx
4
+ from fastapi import Depends, HTTPException, Request, status
5
+ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
6
+ from jose import JWTError, jwt
7
+ from app.config import get_settings
8
+ from app.db.database import get_db
9
+ from sqlalchemy.ext.asyncio import AsyncSession
10
+
11
+ settings = get_settings()
12
+ bearer_scheme = HTTPBearer()
13
+
14
+ _jwks_cache: dict | None = None
15
+ _jwks_cached_at: datetime | None = None
16
+ _JWKS_TTL = timedelta(hours=1) # refresh every hour to pick up key rotations
17
+
18
+
19
+ async def _get_jwks() -> dict:
20
+ global _jwks_cache, _jwks_cached_at
21
+ now = datetime.now(timezone.utc)
22
+ if _jwks_cache and _jwks_cached_at and (now - _jwks_cached_at) < _JWKS_TTL:
23
+ return _jwks_cache
24
+ url = f"{settings.keycloak_url}/realms/{settings.keycloak_realm}/protocol/openid-connect/certs"
25
+ async with httpx.AsyncClient() as client:
26
+ resp = await client.get(url, timeout=10)
27
+ resp.raise_for_status()
28
+ _jwks_cache = resp.json()
29
+ _jwks_cached_at = now
30
+ return _jwks_cache
31
+
32
+
33
+ async def get_current_user(
34
+ credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)],
35
+ ) -> dict:
36
+ token = credentials.credentials
37
+ try:
38
+ jwks = await _get_jwks()
39
+ unverified_header = jwt.get_unverified_header(token)
40
+ key = next(
41
+ (k for k in jwks["keys"] if k["kid"] == unverified_header.get("kid")),
42
+ None,
43
+ )
44
+ if key is None:
45
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unknown signing key")
46
+
47
+ payload = jwt.decode(
48
+ token,
49
+ key,
50
+ algorithms=["RS256"],
51
+ audience=settings.keycloak_client_id,
52
+ options={"verify_exp": True},
53
+ )
54
+ return payload
55
+ except JWTError as exc:
56
+ raise HTTPException(
57
+ status_code=status.HTTP_401_UNAUTHORIZED,
58
+ detail=f"Invalid token: {exc}",
59
+ ) from exc
60
+
61
+
62
+ def user_id(user: Annotated[dict, Depends(get_current_user)]) -> str:
63
+ return user["sub"]
64
+
65
+
66
+ async def user_id_or_bot(request: Request, db: AsyncSession = Depends(get_db)) -> str:
67
+ """Accept either JWT auth or X-User-Id header (when X-Bot-Token matches)."""
68
+ s = get_settings()
69
+ bot_token = request.headers.get("X-Bot-Token", "")
70
+ if bot_token and s.telegram_bot_token and bot_token == s.telegram_bot_token:
71
+ uid = request.headers.get("X-User-Id")
72
+ if uid:
73
+ return uid
74
+ # Fall back to normal JWT auth — extract bearer token from request directly
75
+ auth_header = request.headers.get("Authorization", "")
76
+ if not auth_header.startswith("Bearer "):
77
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated")
78
+ from fastapi.security import HTTPAuthorizationCredentials
79
+ credentials = HTTPAuthorizationCredentials(scheme="Bearer", credentials=auth_header[7:])
80
+ user = await get_current_user(credentials)
81
+ return user["sub"]
@@ -0,0 +1,66 @@
1
+ from pydantic_settings import BaseSettings
2
+ from functools import lru_cache
3
+
4
+
5
+ class Settings(BaseSettings):
6
+ # App
7
+ app_name: str = "Travel Chaos Organizer API"
8
+ debug: bool = False
9
+ upload_dir: str = "/data/uploads"
10
+ app_url: str = "https://tco.app"
11
+
12
+ # Database
13
+ database_url: str = "postgresql+asyncpg://tco:tco@localhost:5432/tco"
14
+
15
+ # Keycloak
16
+ keycloak_url: str = "http://localhost:8080"
17
+ keycloak_realm: str = "travel-chaos"
18
+ keycloak_client_id: str = "tco-backend"
19
+
20
+ # Ollama
21
+ ollama_url: str = "http://localhost:11434"
22
+ ollama_model: str = "llama3.2-vision"
23
+ ollama_timeout: int = 120
24
+
25
+ # Cachly Redis (optional) — caches Ollama parse results by content hash.
26
+ # Set to your Cachly Redis connection string to enable deduplication.
27
+ # Format: redis://:<password>@<host>:<port>/0 or rediss:// for TLS
28
+ cachly_redis_url: str = ""
29
+
30
+ # CORS
31
+ cors_origins: str = "*" # Set to your domain in production: https://yourapp.com
32
+
33
+ # Telegram notifier (optional)
34
+ telegram_bot_token: str = ""
35
+ telegram_chat_id: str = "" # default chat; per-app override below
36
+ tco_telegram_chat_id: str = "" # if set, overrides telegram_chat_id for TCO events
37
+
38
+ # Email (Resend)
39
+ resend_api_key: str = ""
40
+ resend_from: str = "noreply@tco.app"
41
+
42
+ # Admin
43
+ admin_user: str = "admin"
44
+ admin_password: str = "changeme"
45
+
46
+ # Sentry (optional)
47
+ sentry_dsn: str = ""
48
+ environment: str = "production"
49
+
50
+ # Upload
51
+ max_upload_bytes: int = 20 * 1024 * 1024 # 20 MB
52
+
53
+ # Stripe
54
+ stripe_secret_key: str = ""
55
+ stripe_webhook_secret: str = ""
56
+ stripe_pro_price_id: str = "price_YOUR_PRICE_ID"
57
+ stripe_success_url: str = "tco://upgrade/success"
58
+ stripe_cancel_url: str = "tco://upgrade/cancel"
59
+
60
+ class Config:
61
+ env_file = ".env"
62
+
63
+
64
+ @lru_cache
65
+ def get_settings() -> Settings:
66
+ return Settings()
@@ -0,0 +1,20 @@
1
+ from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
2
+ from sqlalchemy.orm import DeclarativeBase
3
+ from app.config import get_settings
4
+
5
+ settings = get_settings()
6
+
7
+ engine = create_async_engine(settings.database_url, echo=settings.debug)
8
+ AsyncSessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
9
+
10
+
11
+ class Base(DeclarativeBase):
12
+ pass
13
+
14
+
15
+ async def get_db() -> AsyncSession:
16
+ async with AsyncSessionLocal() as session:
17
+ try:
18
+ yield session
19
+ finally:
20
+ await session.close()
@@ -0,0 +1,71 @@
1
+ -- Travel Chaos Organizer — Initial Schema
2
+
3
+ CREATE EXTENSION IF NOT EXISTS "pgcrypto";
4
+
5
+ CREATE TABLE IF NOT EXISTS trips (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ user_id TEXT NOT NULL,
8
+ name TEXT NOT NULL,
9
+ description TEXT,
10
+ start_date DATE,
11
+ end_date DATE,
12
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
13
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
14
+ );
15
+
16
+ CREATE INDEX idx_trips_user_id ON trips(user_id);
17
+
18
+ CREATE TYPE item_type AS ENUM (
19
+ 'flight', 'train', 'bus', 'hotel', 'rental_car',
20
+ 'activity', 'transfer', 'document', 'other'
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS trip_items (
24
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
25
+ trip_id UUID NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
26
+ user_id TEXT NOT NULL,
27
+ type item_type NOT NULL DEFAULT 'other',
28
+ title TEXT NOT NULL,
29
+ raw_text TEXT,
30
+ parsed_data JSONB,
31
+ event_at TIMESTAMPTZ,
32
+ event_end_at TIMESTAMPTZ,
33
+ booking_ref TEXT,
34
+ provider TEXT,
35
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
36
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
37
+ );
38
+
39
+ CREATE INDEX idx_trip_items_trip_id ON trip_items(trip_id);
40
+ CREATE INDEX idx_trip_items_event_at ON trip_items(event_at);
41
+ CREATE INDEX idx_trip_items_parsed_data ON trip_items USING gin(parsed_data);
42
+
43
+ CREATE TABLE IF NOT EXISTS attachments (
44
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
45
+ trip_item_id UUID REFERENCES trip_items(id) ON DELETE CASCADE,
46
+ inbox_id UUID,
47
+ user_id TEXT NOT NULL,
48
+ file_path TEXT NOT NULL,
49
+ file_name TEXT NOT NULL,
50
+ mime_type TEXT NOT NULL,
51
+ size_bytes BIGINT,
52
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
53
+ );
54
+
55
+ CREATE INDEX idx_attachments_trip_item_id ON attachments(trip_item_id);
56
+
57
+ CREATE TYPE inbox_status AS ENUM ('pending', 'processed', 'rejected', 'assigned');
58
+
59
+ CREATE TABLE IF NOT EXISTS chaos_inbox (
60
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
61
+ user_id TEXT NOT NULL,
62
+ raw_content TEXT,
63
+ source TEXT,
64
+ status inbox_status NOT NULL DEFAULT 'pending',
65
+ parsed_data JSONB,
66
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
67
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
68
+ );
69
+
70
+ CREATE INDEX idx_chaos_inbox_user_id ON chaos_inbox(user_id);
71
+ CREATE INDEX idx_chaos_inbox_status ON chaos_inbox(status);
@@ -0,0 +1,31 @@
1
+ -- users table: freemium plan tracking. id = Keycloak sub.
2
+ CREATE TABLE IF NOT EXISTS users (
3
+ id TEXT PRIMARY KEY,
4
+ email TEXT,
5
+ plan TEXT NOT NULL DEFAULT 'free',
6
+ plan_expires_at TIMESTAMPTZ,
7
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
8
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
9
+ );
10
+
11
+ -- events table: telemetry. user_id nullable for anonymous/server events.
12
+ CREATE TABLE IF NOT EXISTS events (
13
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
14
+ user_id TEXT,
15
+ event_name TEXT NOT NULL,
16
+ properties JSONB,
17
+ platform TEXT,
18
+ app_version TEXT,
19
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
20
+ );
21
+ CREATE INDEX idx_events_user_id ON events(user_id);
22
+ CREATE INDEX idx_events_name ON events(event_name);
23
+ CREATE INDEX idx_events_created ON events(created_at DESC);
24
+
25
+ -- waitlist: email capture from landing page (no auth required)
26
+ CREATE TABLE IF NOT EXISTS waitlist (
27
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
28
+ email TEXT NOT NULL UNIQUE,
29
+ source TEXT DEFAULT 'landing',
30
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
31
+ );
@@ -0,0 +1,4 @@
1
+ -- Add telegram_chat_id column to users table.
2
+ -- This column was referenced in the application but missing from the initial schema.
3
+ ALTER TABLE users ADD COLUMN IF NOT EXISTS telegram_chat_id TEXT;
4
+ CREATE INDEX IF NOT EXISTS idx_users_telegram_chat_id ON users(telegram_chat_id) WHERE telegram_chat_id IS NOT NULL;
@@ -0,0 +1,5 @@
1
+ -- Add stripe_customer_id to users table.
2
+ -- Used by payments.py to link Stripe customer to user in webhook handlers.
3
+ -- Missing column causes crash on every Stripe webhook (checkout, subscription cancel, renewal, payment failed).
4
+ ALTER TABLE users ADD COLUMN IF NOT EXISTS stripe_customer_id TEXT;
5
+ CREATE INDEX IF NOT EXISTS idx_users_stripe_customer_id ON users(stripe_customer_id) WHERE stripe_customer_id IS NOT NULL;
@@ -0,0 +1,4 @@
1
+ from slowapi import Limiter
2
+ from slowapi.util import get_remote_address
3
+
4
+ limiter = Limiter(key_func=get_remote_address)
@@ -0,0 +1,25 @@
1
+ import logging
2
+ import sys
3
+ import json
4
+ from datetime import datetime, timezone
5
+
6
+
7
+ class JsonFormatter(logging.Formatter):
8
+ def format(self, record: logging.LogRecord) -> str:
9
+ return json.dumps({
10
+ "ts": datetime.now(timezone.utc).isoformat(),
11
+ "level": record.levelname,
12
+ "logger": record.name,
13
+ "msg": record.getMessage(),
14
+ **({"exc": self.formatException(record.exc_info)} if record.exc_info else {}),
15
+ })
16
+
17
+
18
+ def setup_logging(debug: bool = False) -> None:
19
+ level = logging.DEBUG if debug else logging.INFO
20
+ handler = logging.StreamHandler(sys.stdout)
21
+ handler.setFormatter(JsonFormatter())
22
+ logging.basicConfig(level=level, handlers=[handler], force=True)
23
+ # Silence noisy libraries
24
+ for name in ("uvicorn.access", "sqlalchemy.engine", "asyncio"):
25
+ logging.getLogger(name).setLevel(logging.WARNING)
@@ -0,0 +1,77 @@
1
+ from contextlib import asynccontextmanager
2
+ import os
3
+ from pathlib import Path
4
+ from fastapi import FastAPI
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from app.config import get_settings
7
+ from app.routers import trips, items, parse, inbox, mail, events as events_router, waitlist as waitlist_router
8
+ from app.routers import admin as admin_router
9
+ from app.routers import users as users_router
10
+ from app.routers import payments as payments_router
11
+ from app.routers import export as export_router
12
+ from app.routers import cache as cache_router
13
+ from app.services.cache import cachly_configured
14
+ from app.limiter import limiter
15
+ from slowapi import _rate_limit_exceeded_handler
16
+ from slowapi.errors import RateLimitExceeded
17
+ from slowapi.middleware import SlowAPIMiddleware
18
+
19
+ settings = get_settings()
20
+
21
+ APP_VERSION = "0.1.0"
22
+
23
+
24
+ @asynccontextmanager
25
+ async def lifespan(app: FastAPI):
26
+ from app.logging_config import setup_logging
27
+ setup_logging(debug=settings.debug)
28
+ from app.services import sentry as sentry_svc
29
+ sentry_svc.init(settings.sentry_dsn, settings.environment, release=APP_VERSION)
30
+ Path(settings.upload_dir).mkdir(parents=True, exist_ok=True)
31
+ import sys
32
+ if settings.admin_password == "changeme":
33
+ print("⚠️ WARNING: ADMIN_PASSWORD is still 'changeme'. Change it before going live!", file=sys.stderr)
34
+ yield
35
+
36
+
37
+ app = FastAPI(
38
+ title=settings.app_name,
39
+ version=APP_VERSION,
40
+ lifespan=lifespan,
41
+ docs_url="/docs" if settings.debug else None,
42
+ redoc_url="/redoc" if settings.debug else None,
43
+ openapi_url="/openapi.json" if settings.debug else None,
44
+ )
45
+ app.state.limiter = limiter
46
+ app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
47
+ app.add_middleware(SlowAPIMiddleware)
48
+
49
+ app.add_middleware(
50
+ CORSMiddleware,
51
+ allow_origins=settings.cors_origins.split(",") if settings.cors_origins != "*" else ["*"],
52
+ allow_credentials=True,
53
+ allow_methods=["*"],
54
+ allow_headers=["*"],
55
+ )
56
+
57
+ app.include_router(trips.router, prefix="/api/v1")
58
+ app.include_router(items.router, prefix="/api/v1")
59
+ app.include_router(parse.router, prefix="/api/v1")
60
+ app.include_router(inbox.router, prefix="/api/v1")
61
+ app.include_router(mail.router, prefix="/api/v1")
62
+ app.include_router(events_router.router, prefix="/api/v1")
63
+ app.include_router(waitlist_router.router, prefix="/api/v1")
64
+ app.include_router(admin_router.router) # no /api/v1 prefix — admin is at /admin
65
+ app.include_router(users_router.router, prefix="/api/v1")
66
+ app.include_router(payments_router.router, prefix="/api/v1")
67
+ app.include_router(export_router.router, prefix="/api/v1")
68
+ app.include_router(cache_router.router, prefix="/api/v1")
69
+
70
+
71
+ @app.get("/health")
72
+ async def health():
73
+ return {
74
+ "status": "ok",
75
+ "version": APP_VERSION,
76
+ "cachly_cache": "enabled" if cachly_configured() else "disabled",
77
+ }
@@ -0,0 +1,142 @@
1
+ from __future__ import annotations
2
+ from datetime import date, datetime
3
+ from typing import Any, Literal
4
+ from uuid import UUID
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ ItemType = Literal[
9
+ "flight", "train", "bus", "hotel", "rental_car",
10
+ "activity", "transfer", "document", "other"
11
+ ]
12
+
13
+ InboxStatus = Literal["pending", "processed", "rejected", "assigned"]
14
+
15
+
16
+ # ── Trips ──────────────────────────────────────────────────────────────────
17
+
18
+ class TripCreate(BaseModel):
19
+ name: str = Field(..., min_length=1, max_length=200)
20
+ description: str | None = None
21
+ start_date: date | None = None
22
+ end_date: date | None = None
23
+
24
+
25
+ class TripUpdate(BaseModel):
26
+ name: str | None = Field(None, min_length=1, max_length=200)
27
+ description: str | None = None
28
+ start_date: date | None = None
29
+ end_date: date | None = None
30
+
31
+
32
+ class TripOut(BaseModel):
33
+ id: UUID
34
+ user_id: str
35
+ name: str
36
+ description: str | None
37
+ start_date: date | None
38
+ end_date: date | None
39
+ created_at: datetime
40
+ updated_at: datetime
41
+
42
+ model_config = {"from_attributes": True}
43
+
44
+
45
+ # ── Trip Items ─────────────────────────────────────────────────────────────
46
+
47
+ class TripItemCreate(BaseModel):
48
+ type: ItemType = "other"
49
+ title: str = Field(..., min_length=1, max_length=300)
50
+ raw_text: str | None = None
51
+ parsed_data: dict[str, Any] | None = None
52
+ event_at: datetime | None = None
53
+ event_end_at: datetime | None = None
54
+ booking_ref: str | None = None
55
+ provider: str | None = None
56
+
57
+
58
+ class TripItemUpdate(BaseModel):
59
+ type: ItemType | None = None
60
+ title: str | None = Field(None, min_length=1, max_length=300)
61
+ raw_text: str | None = None
62
+ parsed_data: dict[str, Any] | None = None
63
+ event_at: datetime | None = None
64
+ event_end_at: datetime | None = None
65
+ booking_ref: str | None = None
66
+ provider: str | None = None
67
+
68
+
69
+ class TripItemOut(BaseModel):
70
+ id: UUID
71
+ trip_id: UUID
72
+ user_id: str
73
+ type: str
74
+ title: str
75
+ raw_text: str | None
76
+ parsed_data: dict[str, Any] | None
77
+ event_at: datetime | None
78
+ event_end_at: datetime | None
79
+ booking_ref: str | None
80
+ provider: str | None
81
+ created_at: datetime
82
+ updated_at: datetime
83
+
84
+ model_config = {"from_attributes": True}
85
+
86
+
87
+ # ── Attachments ────────────────────────────────────────────────────────────
88
+
89
+ class AttachmentOut(BaseModel):
90
+ id: UUID
91
+ trip_item_id: UUID | None
92
+ inbox_id: UUID | None
93
+ file_name: str
94
+ mime_type: str
95
+ size_bytes: int | None
96
+ created_at: datetime
97
+
98
+ model_config = {"from_attributes": True}
99
+
100
+
101
+ # ── Chaos Inbox ────────────────────────────────────────────────────────────
102
+
103
+ class InboxItemOut(BaseModel):
104
+ id: UUID
105
+ user_id: str
106
+ raw_content: str | None
107
+ source: str | None
108
+ status: str
109
+ parsed_data: dict[str, Any] | None
110
+ created_at: datetime
111
+ updated_at: datetime
112
+
113
+ model_config = {"from_attributes": True}
114
+
115
+
116
+ class InboxAssign(BaseModel):
117
+ trip_id: UUID
118
+ type: ItemType = "other"
119
+
120
+
121
+ # ── Parsing ────────────────────────────────────────────────────────────────
122
+
123
+ class ParsedTravelData(BaseModel):
124
+ type: ItemType = "other"
125
+ title: str = "Unnamed Item"
126
+ booking_ref: str | None = None
127
+ provider: str | None = None
128
+ event_at: str | None = None
129
+ event_end_at: str | None = None
130
+ origin: str | None = None
131
+ destination: str | None = None
132
+ passengers: list[str] | None = None
133
+ confirmation_number: str | None = None
134
+ price: str | None = None
135
+ raw_summary: str | None = None
136
+ confidence: float = 0.0
137
+
138
+
139
+ class ParseResponse(BaseModel):
140
+ parsed: ParsedTravelData
141
+ raw_text: str | None = None
142
+ model_used: str