@cachly-dev/mcp-server 0.10.47 → 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 (309) hide show
  1. package/.github/workflows/eas-build-android.yml +58 -0
  2. package/.github/workflows/eas-update.yml +55 -0
  3. package/STRATEGY.md +209 -0
  4. package/apps/travel-chaos-organizer/.env.example +49 -0
  5. package/apps/travel-chaos-organizer/DEPLOY.md +328 -0
  6. package/apps/travel-chaos-organizer/Makefile +57 -0
  7. package/apps/travel-chaos-organizer/README.md +159 -0
  8. package/apps/travel-chaos-organizer/backend/.env.example +10 -0
  9. package/apps/travel-chaos-organizer/backend/Dockerfile +25 -0
  10. package/apps/travel-chaos-organizer/backend/alembic/env.py +56 -0
  11. package/apps/travel-chaos-organizer/backend/alembic/script.py.mako +24 -0
  12. package/apps/travel-chaos-organizer/backend/alembic/versions/.gitkeep +0 -0
  13. package/apps/travel-chaos-organizer/backend/alembic/versions/0001_stripe_customer_id.py +24 -0
  14. package/apps/travel-chaos-organizer/backend/alembic/versions/0002_telegram_chat_id.py +24 -0
  15. package/apps/travel-chaos-organizer/backend/alembic.ini +39 -0
  16. package/apps/travel-chaos-organizer/backend/app/__init__.py +0 -0
  17. package/apps/travel-chaos-organizer/backend/app/auth/__init__.py +0 -0
  18. package/apps/travel-chaos-organizer/backend/app/auth/keycloak.py +81 -0
  19. package/apps/travel-chaos-organizer/backend/app/config.py +66 -0
  20. package/apps/travel-chaos-organizer/backend/app/db/__init__.py +0 -0
  21. package/apps/travel-chaos-organizer/backend/app/db/database.py +20 -0
  22. package/apps/travel-chaos-organizer/backend/app/db/migrations/001_init.sql +71 -0
  23. package/apps/travel-chaos-organizer/backend/app/db/migrations/002_telemetry_freemium.sql +31 -0
  24. package/apps/travel-chaos-organizer/backend/app/db/migrations/003_telegram_chat_id.sql +4 -0
  25. package/apps/travel-chaos-organizer/backend/app/db/migrations/004_stripe_and_email.sql +5 -0
  26. package/apps/travel-chaos-organizer/backend/app/limiter.py +4 -0
  27. package/apps/travel-chaos-organizer/backend/app/logging_config.py +25 -0
  28. package/apps/travel-chaos-organizer/backend/app/main.py +77 -0
  29. package/apps/travel-chaos-organizer/backend/app/models/__init__.py +0 -0
  30. package/apps/travel-chaos-organizer/backend/app/models/schemas.py +142 -0
  31. package/apps/travel-chaos-organizer/backend/app/routers/__init__.py +0 -0
  32. package/apps/travel-chaos-organizer/backend/app/routers/admin.py +237 -0
  33. package/apps/travel-chaos-organizer/backend/app/routers/cache.py +13 -0
  34. package/apps/travel-chaos-organizer/backend/app/routers/events.py +29 -0
  35. package/apps/travel-chaos-organizer/backend/app/routers/export.py +122 -0
  36. package/apps/travel-chaos-organizer/backend/app/routers/inbox.py +122 -0
  37. package/apps/travel-chaos-organizer/backend/app/routers/items.py +130 -0
  38. package/apps/travel-chaos-organizer/backend/app/routers/mail.py +108 -0
  39. package/apps/travel-chaos-organizer/backend/app/routers/parse.py +303 -0
  40. package/apps/travel-chaos-organizer/backend/app/routers/payments.py +179 -0
  41. package/apps/travel-chaos-organizer/backend/app/routers/trips.py +134 -0
  42. package/apps/travel-chaos-organizer/backend/app/routers/users.py +169 -0
  43. package/apps/travel-chaos-organizer/backend/app/routers/waitlist.py +37 -0
  44. package/apps/travel-chaos-organizer/backend/app/services/__init__.py +0 -0
  45. package/apps/travel-chaos-organizer/backend/app/services/cache.py +131 -0
  46. package/apps/travel-chaos-organizer/backend/app/services/drip.py +128 -0
  47. package/apps/travel-chaos-organizer/backend/app/services/email.py +61 -0
  48. package/apps/travel-chaos-organizer/backend/app/services/errors.py +44 -0
  49. package/apps/travel-chaos-organizer/backend/app/services/notifier.py +10 -0
  50. package/apps/travel-chaos-organizer/backend/app/services/ollama.py +101 -0
  51. package/apps/travel-chaos-organizer/backend/app/services/parser.py +22 -0
  52. package/apps/travel-chaos-organizer/backend/app/services/quota.py +85 -0
  53. package/apps/travel-chaos-organizer/backend/app/services/sentry.py +11 -0
  54. package/apps/travel-chaos-organizer/backend/app/services/telemetry.py +35 -0
  55. package/apps/travel-chaos-organizer/backend/pytest.ini +4 -0
  56. package/apps/travel-chaos-organizer/backend/requirements-dev.txt +6 -0
  57. package/apps/travel-chaos-organizer/backend/requirements.txt +25 -0
  58. package/apps/travel-chaos-organizer/backend/tests/__init__.py +0 -0
  59. package/apps/travel-chaos-organizer/backend/tests/conftest.py +223 -0
  60. package/apps/travel-chaos-organizer/backend/tests/test_admin.py +99 -0
  61. package/apps/travel-chaos-organizer/backend/tests/test_cache.py +142 -0
  62. package/apps/travel-chaos-organizer/backend/tests/test_drip.py +32 -0
  63. package/apps/travel-chaos-organizer/backend/tests/test_e2e_chain.py +121 -0
  64. package/apps/travel-chaos-organizer/backend/tests/test_export.py +48 -0
  65. package/apps/travel-chaos-organizer/backend/tests/test_inbox.py +107 -0
  66. package/apps/travel-chaos-organizer/backend/tests/test_items.py +138 -0
  67. package/apps/travel-chaos-organizer/backend/tests/test_mail.py +113 -0
  68. package/apps/travel-chaos-organizer/backend/tests/test_ollama.py +145 -0
  69. package/apps/travel-chaos-organizer/backend/tests/test_parse_url.py +98 -0
  70. package/apps/travel-chaos-organizer/backend/tests/test_parser.py +72 -0
  71. package/apps/travel-chaos-organizer/backend/tests/test_payments.py +163 -0
  72. package/apps/travel-chaos-organizer/backend/tests/test_quota.py +46 -0
  73. package/apps/travel-chaos-organizer/backend/tests/test_search.py +67 -0
  74. package/apps/travel-chaos-organizer/backend/tests/test_trips.py +123 -0
  75. package/apps/travel-chaos-organizer/backend/tests/test_users.py +52 -0
  76. package/apps/travel-chaos-organizer/backups/.gitkeep +0 -0
  77. package/apps/travel-chaos-organizer/caddy/Caddyfile +27 -0
  78. package/apps/travel-chaos-organizer/docker-compose.node1.yml +31 -0
  79. package/apps/travel-chaos-organizer/docker-compose.override.yml +14 -0
  80. package/apps/travel-chaos-organizer/docker-compose.yml +128 -0
  81. package/apps/travel-chaos-organizer/frontend/.env.example +13 -0
  82. package/apps/travel-chaos-organizer/frontend/app/(app)/_layout.tsx +80 -0
  83. package/apps/travel-chaos-organizer/frontend/app/(app)/inbox.tsx +166 -0
  84. package/apps/travel-chaos-organizer/frontend/app/(app)/search.tsx +130 -0
  85. package/apps/travel-chaos-organizer/frontend/app/(app)/settings.tsx +381 -0
  86. package/apps/travel-chaos-organizer/frontend/app/(app)/trips/[id].tsx +346 -0
  87. package/apps/travel-chaos-organizer/frontend/app/(app)/trips.tsx +363 -0
  88. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/cancel.tsx +34 -0
  89. package/apps/travel-chaos-organizer/frontend/app/(app)/upgrade/success.tsx +35 -0
  90. package/apps/travel-chaos-organizer/frontend/app/(auth)/login.tsx +91 -0
  91. package/apps/travel-chaos-organizer/frontend/app/_layout.tsx +71 -0
  92. package/apps/travel-chaos-organizer/frontend/app/auth.tsx +29 -0
  93. package/apps/travel-chaos-organizer/frontend/app/index.tsx +16 -0
  94. package/apps/travel-chaos-organizer/frontend/app/onboarding.tsx +119 -0
  95. package/apps/travel-chaos-organizer/frontend/app.config.ts +54 -0
  96. package/apps/travel-chaos-organizer/frontend/assets/adaptive-icon.svg +66 -0
  97. package/apps/travel-chaos-organizer/frontend/assets/icon.svg +79 -0
  98. package/apps/travel-chaos-organizer/frontend/assets/splash.svg +15 -0
  99. package/apps/travel-chaos-organizer/frontend/components/BottomSheet.tsx +102 -0
  100. package/apps/travel-chaos-organizer/frontend/components/ConfettiCannon.tsx +107 -0
  101. package/apps/travel-chaos-organizer/frontend/components/ConfirmDialog.tsx +109 -0
  102. package/apps/travel-chaos-organizer/frontend/components/ErrorBoundary.tsx +45 -0
  103. package/apps/travel-chaos-organizer/frontend/components/FileUploadButton.tsx +19 -0
  104. package/apps/travel-chaos-organizer/frontend/components/ImportSheet.tsx +155 -0
  105. package/apps/travel-chaos-organizer/frontend/components/OfflineBadge.tsx +55 -0
  106. package/apps/travel-chaos-organizer/frontend/components/QRCodeViewer.tsx +80 -0
  107. package/apps/travel-chaos-organizer/frontend/components/ScoreRing.tsx +39 -0
  108. package/apps/travel-chaos-organizer/frontend/components/Screen.tsx +39 -0
  109. package/apps/travel-chaos-organizer/frontend/components/Skeleton.tsx +70 -0
  110. package/apps/travel-chaos-organizer/frontend/components/SwipeableInboxItem.tsx +121 -0
  111. package/apps/travel-chaos-organizer/frontend/components/SwipeableTimelineItem.tsx +70 -0
  112. package/apps/travel-chaos-organizer/frontend/components/TimelineItem.tsx +73 -0
  113. package/apps/travel-chaos-organizer/frontend/components/TimelineItemDetail.tsx +130 -0
  114. package/apps/travel-chaos-organizer/frontend/components/Toast.tsx +60 -0
  115. package/apps/travel-chaos-organizer/frontend/components/ToastContext.tsx +42 -0
  116. package/apps/travel-chaos-organizer/frontend/components/TripCard.tsx +146 -0
  117. package/apps/travel-chaos-organizer/frontend/components/TripQRModal.tsx +70 -0
  118. package/apps/travel-chaos-organizer/frontend/components/UpgradeBanner.tsx +44 -0
  119. package/apps/travel-chaos-organizer/frontend/eas.json +22 -0
  120. package/apps/travel-chaos-organizer/frontend/hooks/useInbox.ts +24 -0
  121. package/apps/travel-chaos-organizer/frontend/hooks/useNetworkSync.ts +60 -0
  122. package/apps/travel-chaos-organizer/frontend/hooks/useTrips.ts +112 -0
  123. package/apps/travel-chaos-organizer/frontend/lib/api.ts +223 -0
  124. package/apps/travel-chaos-organizer/frontend/lib/auth.ts +93 -0
  125. package/apps/travel-chaos-organizer/frontend/lib/db.ts +86 -0
  126. package/apps/travel-chaos-organizer/frontend/lib/fileCache.ts +54 -0
  127. package/apps/travel-chaos-organizer/frontend/lib/haptics.ts +13 -0
  128. package/apps/travel-chaos-organizer/frontend/lib/notifications.ts +112 -0
  129. package/apps/travel-chaos-organizer/frontend/lib/offlineQueue.ts +145 -0
  130. package/apps/travel-chaos-organizer/frontend/lib/quota.tsx +43 -0
  131. package/apps/travel-chaos-organizer/frontend/lib/sentry.ts +28 -0
  132. package/apps/travel-chaos-organizer/frontend/lib/shareIntent.ts +44 -0
  133. package/apps/travel-chaos-organizer/frontend/package-lock.json +13037 -0
  134. package/apps/travel-chaos-organizer/frontend/package.json +44 -0
  135. package/apps/travel-chaos-organizer/frontend/tsconfig.json +23 -0
  136. package/apps/travel-chaos-organizer/keycloak/realm-export.json +59 -0
  137. package/apps/travel-chaos-organizer/landing/index.html +773 -0
  138. package/apps/travel-chaos-organizer/scripts/backup.sh +14 -0
  139. package/apps/travel-chaos-organizer/scripts/configure-env.sh +82 -0
  140. package/apps/travel-chaos-organizer/scripts/deploy.sh +54 -0
  141. package/apps/travel-chaos-organizer/scripts/health-check.sh +36 -0
  142. package/apps/travel-chaos-organizer/scripts/migrate.sh +7 -0
  143. package/apps/travel-chaos-organizer/scripts/server-setup.sh +85 -0
  144. package/apps/travel-chaos-organizer/scripts/smoke-test.sh +29 -0
  145. package/apps/travel-chaos-organizer/scripts/verify-tracking.sh +71 -0
  146. package/apps/travel-chaos-organizer/telegram-bot/.env.example +3 -0
  147. package/apps/travel-chaos-organizer/telegram-bot/Dockerfile +6 -0
  148. package/apps/travel-chaos-organizer/telegram-bot/bot.py +504 -0
  149. package/apps/travel-chaos-organizer/telegram-bot/requirements.txt +3 -0
  150. package/dist/packages/telegram-notify/client.d.ts +19 -0
  151. package/dist/packages/telegram-notify/client.js +75 -0
  152. package/dist/packages/telegram-notify/client.js.map +1 -0
  153. package/dist/src/__tests__/auth.test.js.map +1 -0
  154. package/dist/{__tests__ → src/__tests__}/brain-flow.test.js +143 -2
  155. package/dist/src/__tests__/brain-flow.test.js.map +1 -0
  156. package/dist/src/__tests__/confidence.test.js.map +1 -0
  157. package/dist/src/__tests__/e2e/api-auth.test.js.map +1 -0
  158. package/dist/src/__tests__/e2e/api-health.test.js.map +1 -0
  159. package/dist/src/__tests__/e2e/cli.test.js.map +1 -0
  160. package/dist/src/__tests__/e2e/config.js.map +1 -0
  161. package/dist/src/__tests__/e2e/funnel.test.js.map +1 -0
  162. package/dist/src/__tests__/e2e/mcp-tools.test.js.map +1 -0
  163. package/dist/src/__tests__/e2e/provisioning.test.js.map +1 -0
  164. package/dist/{__tests__ → src/__tests__}/handlers-unit.test.js +1 -1
  165. package/dist/src/__tests__/handlers-unit.test.js.map +1 -0
  166. package/dist/src/__tests__/namespace.test.js.map +1 -0
  167. package/dist/src/auth.js.map +1 -0
  168. package/dist/src/ckg.js.map +1 -0
  169. package/dist/src/confidence.js.map +1 -0
  170. package/dist/src/embeddings.js.map +1 -0
  171. package/dist/src/handlers/advanced.js.map +1 -0
  172. package/dist/{handlers → src/handlers}/brain.js +135 -31
  173. package/dist/src/handlers/brain.js.map +1 -0
  174. package/dist/src/handlers/cache.js.map +1 -0
  175. package/dist/{handlers → src/handlers}/context.js +9 -4
  176. package/dist/src/handlers/context.js.map +1 -0
  177. package/dist/{handlers → src/handlers}/fedbrain.d.ts +6 -0
  178. package/dist/{handlers → src/handlers}/fedbrain.js +15 -2
  179. package/dist/src/handlers/fedbrain.js.map +1 -0
  180. package/dist/src/handlers/instances.js.map +1 -0
  181. package/dist/{handlers → src/handlers}/roadmap.js +8 -4
  182. package/dist/src/handlers/roadmap.js.map +1 -0
  183. package/dist/src/handlers/syndicate.js.map +1 -0
  184. package/dist/src/handlers/tco.d.ts +16 -0
  185. package/dist/src/handlers/tco.js +303 -0
  186. package/dist/src/handlers/tco.js.map +1 -0
  187. package/dist/{handlers → src/handlers}/team.js +1 -1
  188. package/dist/src/handlers/team.js.map +1 -0
  189. package/dist/{index.js → src/index.js} +145 -97
  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/{search.js → src/search.js} +3 -3
  196. package/dist/src/search.js.map +1 -0
  197. package/dist/src/search.test.js.map +1 -0
  198. package/dist/{tools.d.ts → src/tools.d.ts} +196 -0
  199. package/dist/{tools.js → src/tools.js} +178 -0
  200. package/dist/src/tools.js.map +1 -0
  201. package/dist/src/utils.js.map +1 -0
  202. package/package.json +3 -2
  203. package/packages/telegram-notify/SPLIT_PLAN.md +106 -0
  204. package/packages/telegram-notify/client.ts +88 -0
  205. package/packages/telegram-notify/emojis.json +22 -0
  206. package/packages/telegram-notify/pyproject.toml +17 -0
  207. package/packages/telegram-notify/telegram_notify/__init__.py +3 -0
  208. package/packages/telegram-notify/telegram_notify/client.py +87 -0
  209. package/packages/telegram-notify/telegram_notify/emojis.json +22 -0
  210. package/scripts/generate-icons.js +28 -0
  211. package/smithery.yaml +21 -10
  212. package/src/__tests__/brain-flow.test.ts +176 -2
  213. package/src/__tests__/handlers-unit.test.ts +1 -1
  214. package/src/handlers/brain.ts +139 -32
  215. package/src/handlers/context.ts +9 -4
  216. package/src/handlers/fedbrain.ts +17 -2
  217. package/src/handlers/roadmap.ts +8 -4
  218. package/src/handlers/tco.ts +347 -0
  219. package/src/handlers/team.ts +1 -1
  220. package/src/index.ts +137 -147
  221. package/src/notifier.ts +5 -0
  222. package/src/search.ts +3 -3
  223. package/src/tools.ts +191 -0
  224. package/tsconfig.json +2 -3
  225. package/dist/__tests__/auth.test.js.map +0 -1
  226. package/dist/__tests__/brain-flow.test.js.map +0 -1
  227. package/dist/__tests__/brain-predict-failures.test.d.ts +0 -17
  228. package/dist/__tests__/brain-predict-failures.test.js +0 -339
  229. package/dist/__tests__/brain-predict-failures.test.js.map +0 -1
  230. package/dist/__tests__/confidence.test.js.map +0 -1
  231. package/dist/__tests__/e2e/api-auth.test.js.map +0 -1
  232. package/dist/__tests__/e2e/api-health.test.js.map +0 -1
  233. package/dist/__tests__/e2e/cli.test.js.map +0 -1
  234. package/dist/__tests__/e2e/config.js.map +0 -1
  235. package/dist/__tests__/e2e/funnel.test.js.map +0 -1
  236. package/dist/__tests__/e2e/mcp-tools.test.js.map +0 -1
  237. package/dist/__tests__/e2e/provisioning.test.js.map +0 -1
  238. package/dist/__tests__/handlers-unit.test.js.map +0 -1
  239. package/dist/__tests__/namespace.test.js.map +0 -1
  240. package/dist/auth.js.map +0 -1
  241. package/dist/ckg.js.map +0 -1
  242. package/dist/confidence.js.map +0 -1
  243. package/dist/embeddings.js.map +0 -1
  244. package/dist/handlers/advanced.js.map +0 -1
  245. package/dist/handlers/brain.js.map +0 -1
  246. package/dist/handlers/cache.js.map +0 -1
  247. package/dist/handlers/context.js.map +0 -1
  248. package/dist/handlers/fedbrain.js.map +0 -1
  249. package/dist/handlers/instances.js.map +0 -1
  250. package/dist/handlers/roadmap.js.map +0 -1
  251. package/dist/handlers/syndicate.js.map +0 -1
  252. package/dist/handlers/team.js.map +0 -1
  253. package/dist/index.js.map +0 -1
  254. package/dist/namespace.js.map +0 -1
  255. package/dist/search.js.map +0 -1
  256. package/dist/search.test.js.map +0 -1
  257. package/dist/tools.js.map +0 -1
  258. package/dist/utils.js.map +0 -1
  259. package/src/__tests__/brain-predict-failures.test.ts +0 -430
  260. /package/dist/{__tests__ → src/__tests__}/auth.test.d.ts +0 -0
  261. /package/dist/{__tests__ → src/__tests__}/auth.test.js +0 -0
  262. /package/dist/{__tests__ → src/__tests__}/brain-flow.test.d.ts +0 -0
  263. /package/dist/{__tests__ → src/__tests__}/confidence.test.d.ts +0 -0
  264. /package/dist/{__tests__ → src/__tests__}/confidence.test.js +0 -0
  265. /package/dist/{__tests__ → src/__tests__}/e2e/api-auth.test.d.ts +0 -0
  266. /package/dist/{__tests__ → src/__tests__}/e2e/api-auth.test.js +0 -0
  267. /package/dist/{__tests__ → src/__tests__}/e2e/api-health.test.d.ts +0 -0
  268. /package/dist/{__tests__ → src/__tests__}/e2e/api-health.test.js +0 -0
  269. /package/dist/{__tests__ → src/__tests__}/e2e/cli.test.d.ts +0 -0
  270. /package/dist/{__tests__ → src/__tests__}/e2e/cli.test.js +0 -0
  271. /package/dist/{__tests__ → src/__tests__}/e2e/config.d.ts +0 -0
  272. /package/dist/{__tests__ → src/__tests__}/e2e/config.js +0 -0
  273. /package/dist/{__tests__ → src/__tests__}/e2e/funnel.test.d.ts +0 -0
  274. /package/dist/{__tests__ → src/__tests__}/e2e/funnel.test.js +0 -0
  275. /package/dist/{__tests__ → src/__tests__}/e2e/mcp-tools.test.d.ts +0 -0
  276. /package/dist/{__tests__ → src/__tests__}/e2e/mcp-tools.test.js +0 -0
  277. /package/dist/{__tests__ → src/__tests__}/e2e/provisioning.test.d.ts +0 -0
  278. /package/dist/{__tests__ → src/__tests__}/e2e/provisioning.test.js +0 -0
  279. /package/dist/{__tests__ → src/__tests__}/handlers-unit.test.d.ts +0 -0
  280. /package/dist/{__tests__ → src/__tests__}/namespace.test.d.ts +0 -0
  281. /package/dist/{__tests__ → src/__tests__}/namespace.test.js +0 -0
  282. /package/dist/{auth.d.ts → src/auth.d.ts} +0 -0
  283. /package/dist/{auth.js → src/auth.js} +0 -0
  284. /package/dist/{ckg.d.ts → src/ckg.d.ts} +0 -0
  285. /package/dist/{ckg.js → src/ckg.js} +0 -0
  286. /package/dist/{confidence.d.ts → src/confidence.d.ts} +0 -0
  287. /package/dist/{confidence.js → src/confidence.js} +0 -0
  288. /package/dist/{embeddings.d.ts → src/embeddings.d.ts} +0 -0
  289. /package/dist/{embeddings.js → src/embeddings.js} +0 -0
  290. /package/dist/{handlers → src/handlers}/advanced.d.ts +0 -0
  291. /package/dist/{handlers → src/handlers}/advanced.js +0 -0
  292. /package/dist/{handlers → src/handlers}/brain.d.ts +0 -0
  293. /package/dist/{handlers → src/handlers}/cache.d.ts +0 -0
  294. /package/dist/{handlers → src/handlers}/cache.js +0 -0
  295. /package/dist/{handlers → src/handlers}/context.d.ts +0 -0
  296. /package/dist/{handlers → src/handlers}/instances.d.ts +0 -0
  297. /package/dist/{handlers → src/handlers}/instances.js +0 -0
  298. /package/dist/{handlers → src/handlers}/roadmap.d.ts +0 -0
  299. /package/dist/{handlers → src/handlers}/syndicate.d.ts +0 -0
  300. /package/dist/{handlers → src/handlers}/syndicate.js +0 -0
  301. /package/dist/{handlers → src/handlers}/team.d.ts +0 -0
  302. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
  303. /package/dist/{namespace.d.ts → src/namespace.d.ts} +0 -0
  304. /package/dist/{namespace.js → src/namespace.js} +0 -0
  305. /package/dist/{search.d.ts → src/search.d.ts} +0 -0
  306. /package/dist/{search.test.d.ts → src/search.test.d.ts} +0 -0
  307. /package/dist/{search.test.js → src/search.test.js} +0 -0
  308. /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
  309. /package/dist/{utils.js → src/utils.js} +0 -0
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { jwtExpiryMs, checkJwt, handleApiError } from './auth.js';
3
+ import { handleTcoTool } from './handlers/tco.js';
4
+ import { notify } from './notifier.js';
3
5
  import { readFile } from 'node:fs/promises';
4
6
  import { fileURLToPath } from 'node:url';
5
7
  // True only when this file is the entry point (not imported by tests or other modules).
@@ -34,26 +36,40 @@ const _isMain = process.argv[1] != null &&
34
36
  * ── Auth & Status ────────────────────────────────────────────────────────────
35
37
  * • get_api_status – check API health + JWT auth info (Keycloak)
36
38
  *
39
+ * ── Travel Chaos Organizer (TCO) ────────────────────────────────────────────
40
+ * • tco_list_trips – list all trips
41
+ * • tco_create_trip – create a new trip
42
+ * • tco_get_timeline – get timeline items for a trip
43
+ * • tco_delete_trip – delete a trip
44
+ * • tco_inbox_list – list Chaos Inbox items
45
+ * • tco_inbox_assign – assign inbox item to a trip
46
+ * • tco_inbox_reject – reject/discard an inbox item
47
+ * • tco_parse_url – fetch a URL and parse with Ollama
48
+ * • tco_import_email – import raw email text
49
+ * (Requires TCO_API_URL env var; auth forwarded from CACHLY_JWT — same Keycloak realm)
50
+ *
37
51
  * Configuration (env vars):
38
52
  * CACHLY_API_URL – default https://api.cachly.dev
39
53
  * CACHLY_JWT – your JWT (Keycloak access token)
40
54
  * CACHLY_EMBED_PROVIDER – embedding backend: openai (default), gemini, mistral, cohere, ollama, cachly (server fallback)
41
55
  * CACHLY_EMBED_MODEL – override embedding model (optional)
56
+ * TCO_API_URL – Travel Chaos Organizer backend URL (optional, enables tco_* tools)
42
57
  */
43
58
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
44
59
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
45
60
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
46
61
  import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
47
62
  import { Redis } from 'ioredis';
48
- import { AsyncLocalStorage } from 'node:async_hooks';
49
63
  // ── Config ────────────────────────────────────────────────────────────────────
50
64
  const API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
51
65
  let JWT = process.env.CACHLY_JWT ?? '';
52
66
  const EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
53
- const CURRENT_VERSION = '0.10.47';
54
- const reqCtx = new AsyncLocalStorage();
55
- function getJWT() { return reqCtx.getStore()?.jwt || JWT; }
56
- function getDefaultInstanceId() { return reqCtx.getStore()?.instanceId || _defaultInstanceId; }
67
+ const CURRENT_VERSION = '0.10.49';
68
+ // Max time to wait for a freshly-provisioned instance to become "running" before
69
+ // giving up. Free-tier provisioning in high-latency regions can take 45–90s, so the
70
+ // old hard-coded 25s caused spurious "instance_not_reachable" on first run.
71
+ // Override via CACHLY_PROVISION_TIMEOUT_MS.
72
+ const PROVISION_TIMEOUT_MS = Number(process.env.CACHLY_PROVISION_TIMEOUT_MS ?? 90_000);
57
73
  // ── Default Instance Resolution (for Smithery & single-credential setups) ────
58
74
  // When CACHLY_BRAIN_INSTANCE_ID is set, tools can omit the instance_id parameter.
59
75
  // When neither is set, we auto-fetch the first running instance once per process.
@@ -61,13 +77,9 @@ let _defaultInstanceId = process.env.CACHLY_BRAIN_INSTANCE_ID ?? '';
61
77
  // Timestamp of last failed fetch — retries after 30 s (not permanently blocked).
62
78
  let _defaultInstanceLastAttempt = 0;
63
79
  async function resolveDefaultInstanceId() {
64
- // In an HTTP request scope the instance ID is fixed by the caller's header.
65
- const scopedId = reqCtx.getStore()?.instanceId;
66
- if (scopedId)
67
- return scopedId;
68
80
  if (_defaultInstanceId)
69
81
  return _defaultInstanceId;
70
- if (!getJWT())
82
+ if (!JWT)
71
83
  return '';
72
84
  // Cooldown: don't hammer the API on every tool call after a transient failure.
73
85
  const now = Date.now();
@@ -76,7 +88,7 @@ async function resolveDefaultInstanceId() {
76
88
  _defaultInstanceLastAttempt = now;
77
89
  try {
78
90
  const res = await fetch(`${API_URL}/api/v1/instances`, {
79
- headers: { Authorization: `Bearer ${getJWT()}`, Accept: 'application/json' },
91
+ headers: { Authorization: `Bearer ${JWT}`, Accept: 'application/json' },
80
92
  signal: AbortSignal.timeout(5000),
81
93
  });
82
94
  if (!res.ok)
@@ -155,6 +167,42 @@ async function pollDeviceFlow(flow) {
155
167
  setEmbedJwt(apiKey); // keep embeddings.ts in sync
156
168
  _deviceFlow = null;
157
169
  sendFunnelEvent('device_flow_completed');
170
+ // Persist the API key to ~/.claude/mcp.json so subsequent MCP server restarts
171
+ // keep the token. Without this, JWT is lost on restart and brain_recall_count
172
+ // is never incremented (telemetry has no api_key → no tenant resolution).
173
+ void (async () => {
174
+ try {
175
+ const { writeFile, mkdir, readFile } = await import('node:fs/promises');
176
+ const { existsSync } = await import('node:fs');
177
+ const { resolve, dirname } = await import('node:path');
178
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? '';
179
+ if (!home)
180
+ return;
181
+ const configPath = resolve(home, '.claude', 'mcp.json');
182
+ let cfg = {};
183
+ if (existsSync(configPath)) {
184
+ try {
185
+ cfg = JSON.parse(await readFile(configPath, 'utf-8'));
186
+ }
187
+ catch { /* corrupt — start fresh */ }
188
+ }
189
+ cfg.mcpServers ??= {};
190
+ const existing = cfg.mcpServers['cachly'];
191
+ if (existing) {
192
+ existing.env ??= {};
193
+ existing.env['CACHLY_JWT'] = apiKey;
194
+ }
195
+ else {
196
+ cfg.mcpServers['cachly'] = {
197
+ command: 'npx', args: ['-y', '@cachly-dev/mcp-server@latest'],
198
+ env: { CACHLY_JWT: apiKey },
199
+ };
200
+ }
201
+ await mkdir(dirname(configPath), { recursive: true });
202
+ await writeFile(configPath, JSON.stringify(cfg, null, 2), 'utf-8');
203
+ }
204
+ catch { /* non-critical — don't break the flow on filesystem errors */ }
205
+ })();
158
206
  // Auto-provision: find or create the user's brain instance.
159
207
  _defaultInstanceLastAttempt = 0;
160
208
  await resolveDefaultInstanceId();
@@ -162,7 +210,7 @@ async function pollDeviceFlow(flow) {
162
210
  try {
163
211
  const autoRes = await fetch(`${API_URL}/api/v1/instances/auto`, {
164
212
  method: 'POST',
165
- headers: { Authorization: `Bearer ${getJWT()}`, 'Content-Type': 'application/json' },
213
+ headers: { Authorization: `Bearer ${JWT}`, 'Content-Type': 'application/json' },
166
214
  signal: AbortSignal.timeout(12000),
167
215
  });
168
216
  if (autoRes.ok) {
@@ -175,7 +223,7 @@ async function pollDeviceFlow(flow) {
175
223
  // Log so telemetry can detect free-tier auto-provision failures
176
224
  void fetch(`${API_URL}/api/v1/telemetry/mcp`, {
177
225
  method: 'POST',
178
- headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${getJWT()}` },
226
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${JWT}` },
179
227
  body: JSON.stringify({ event: 'auto_provision_failed', status: autoRes.status, version: CURRENT_VERSION }),
180
228
  signal: AbortSignal.timeout(3000),
181
229
  }).catch(() => { });
@@ -188,7 +236,7 @@ async function pollDeviceFlow(flow) {
188
236
  if (_defaultInstanceId) {
189
237
  try {
190
238
  const checkRes = await fetch(`${API_URL}/api/v1/instances/${_defaultInstanceId}`, {
191
- headers: { Authorization: `Bearer ${getJWT()}`, Accept: 'application/json' },
239
+ headers: { Authorization: `Bearer ${JWT}`, Accept: 'application/json' },
192
240
  signal: AbortSignal.timeout(4000),
193
241
  });
194
242
  if (checkRes.ok) {
@@ -199,7 +247,7 @@ async function pollDeviceFlow(flow) {
199
247
  while (checkInst.status === 'provisioning' && Date.now() < pDeadline) {
200
248
  await new Promise(r => setTimeout(r, 3000));
201
249
  const rr = await fetch(`${API_URL}/api/v1/instances/${_defaultInstanceId}`, {
202
- headers: { Authorization: `Bearer ${getJWT()}`, Accept: 'application/json' },
250
+ headers: { Authorization: `Bearer ${JWT}`, Accept: 'application/json' },
203
251
  signal: AbortSignal.timeout(4000),
204
252
  }).catch(() => null);
205
253
  if (rr?.ok) {
@@ -215,6 +263,37 @@ async function pollDeviceFlow(flow) {
215
263
  }
216
264
  catch { /* non-fatal — getConnection will handle the wait if still provisioning */ }
217
265
  }
266
+ // Persist instance_id to ~/.claude/mcp.json after it's known.
267
+ // Without this, every restart loses CACHLY_BRAIN_INSTANCE_ID → resolveDefaultInstanceId
268
+ // makes an extra API call on every startup (with 30s cooldown on failure).
269
+ if (_defaultInstanceId) {
270
+ void (async () => {
271
+ try {
272
+ const { writeFile, readFile } = await import('node:fs/promises');
273
+ const { existsSync } = await import('node:fs');
274
+ const { resolve } = await import('node:path');
275
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? '';
276
+ if (!home)
277
+ return;
278
+ const configPath = resolve(home, '.claude', 'mcp.json');
279
+ if (!existsSync(configPath))
280
+ return;
281
+ let cfg = {};
282
+ try {
283
+ cfg = JSON.parse(await readFile(configPath, 'utf-8'));
284
+ }
285
+ catch {
286
+ return;
287
+ }
288
+ const entry = cfg.mcpServers?.['cachly'];
289
+ if (entry?.env) {
290
+ entry.env['CACHLY_BRAIN_INSTANCE_ID'] = _defaultInstanceId;
291
+ await writeFile(configPath, JSON.stringify(cfg, null, 2), 'utf-8');
292
+ }
293
+ }
294
+ catch { /* non-critical */ }
295
+ })();
296
+ }
218
297
  return 'done';
219
298
  }
220
299
  if (data.error === 'slow_down')
@@ -243,12 +322,12 @@ async function getConnection(instance_id) {
243
322
  }
244
323
  if (pool.has(instance_id))
245
324
  return pool.get(instance_id);
246
- // Fetch instance, waiting up to 25 s if it is still provisioning.
325
+ // Fetch instance, waiting up to PROVISION_TIMEOUT_MS if it is still provisioning.
247
326
  // This covers the zero-friction path: device-flow auth → auto-provision → first tool call
248
327
  // all happen in quick succession and the instance isn't running yet.
249
328
  let inst = await apiFetch(`/api/v1/instances/${instance_id}`);
250
329
  if (inst.status === 'provisioning') {
251
- const deadline = Date.now() + 25_000;
330
+ const deadline = Date.now() + PROVISION_TIMEOUT_MS;
252
331
  while (inst.status === 'provisioning' && Date.now() < deadline) {
253
332
  await new Promise(r => setTimeout(r, 3000));
254
333
  inst = await apiFetch(`/api/v1/instances/${instance_id}`);
@@ -258,7 +337,7 @@ async function getConnection(instance_id) {
258
337
  // Telemetry: unreachable instance so the team can proactively investigate
259
338
  void fetch(`${API_URL}/api/v1/telemetry/mcp`, {
260
339
  method: 'POST',
261
- headers: { 'Content-Type': 'application/json', ...(getJWT() ? { Authorization: `Bearer ${getJWT()}` } : {}) },
340
+ headers: { 'Content-Type': 'application/json', ...(JWT ? { Authorization: `Bearer ${JWT}` } : {}) },
262
341
  body: JSON.stringify({ event: 'instance_not_reachable', instance_id, status: inst.status, version: CURRENT_VERSION }),
263
342
  signal: AbortSignal.timeout(3000),
264
343
  }).catch(() => { });
@@ -310,6 +389,7 @@ async function getConnection(instance_id) {
310
389
  host: inst.host,
311
390
  port: inst.port,
312
391
  password: password || undefined,
392
+ commandTimeout: 8000,
313
393
  ...(tlsEnabled ? { tls: {} } : {}),
314
394
  lazyConnect: true,
315
395
  enableReadyCheck: true,
@@ -327,7 +407,7 @@ async function getConnection(instance_id) {
327
407
  const msg = err instanceof Error ? err.message : String(err);
328
408
  void fetch(`${API_URL}/api/v1/telemetry/mcp`, {
329
409
  method: 'POST',
330
- headers: { 'Content-Type': 'application/json', ...(getJWT() ? { Authorization: `Bearer ${getJWT()}` } : {}) },
410
+ headers: { 'Content-Type': 'application/json', ...(JWT ? { Authorization: `Bearer ${JWT}` } : {}) },
331
411
  body: JSON.stringify({ event: 'redis_connect_failed', instance_id, host: inst.host, port: inst.port, error: msg, version: CURRENT_VERSION }),
332
412
  signal: AbortSignal.timeout(3000),
333
413
  }).catch(() => { });
@@ -341,14 +421,14 @@ async function getConnection(instance_id) {
341
421
  }
342
422
  // ── API helper ────────────────────────────────────────────────────────────────
343
423
  async function apiFetch(path, options = {}) {
344
- checkJwt(getJWT());
424
+ checkJwt(JWT);
345
425
  const timeoutMs = Number(process.env.CACHLY_API_TIMEOUT_MS ?? 15_000);
346
426
  const res = await fetch(`${API_URL}${path}`, {
347
427
  ...options,
348
428
  signal: AbortSignal.timeout(timeoutMs),
349
429
  headers: {
350
430
  'Content-Type': 'application/json',
351
- Authorization: `Bearer ${getJWT()}`,
431
+ Authorization: `Bearer ${JWT}`,
352
432
  ...options.headers,
353
433
  },
354
434
  });
@@ -369,7 +449,7 @@ import { handleTeamTool } from './handlers/team.js';
369
449
  import { handleRoadmapTool } from './handlers/roadmap.js';
370
450
  import { handleAdvancedTool } from './handlers/advanced.js';
371
451
  import { handleSyndicateTool } from './handlers/syndicate.js';
372
- import { handleFedbrainTool } from './handlers/fedbrain.js';
452
+ import { handleFedbrainTool, _lastBrainFromGitCounts } from './handlers/fedbrain.js';
373
453
  // ── Tools (imported from tools.ts) ─────────────────────────────────────────
374
454
  import { TOOLS } from './tools.js';
375
455
  // ── Handlers ──────────────────────────────────────────────────────────────────
@@ -392,7 +472,7 @@ function sendFunnelEvent(event, extra) {
392
472
  headers: { 'Content-Type': 'application/json' },
393
473
  body: JSON.stringify({
394
474
  event, version: CURRENT_VERSION, editor: detectEditor(),
395
- ...(getJWT() ? { jwt: getJWT() } : {}),
475
+ ...(JWT ? { jwt: JWT } : {}),
396
476
  ...extra,
397
477
  }),
398
478
  signal: AbortSignal.timeout(3000),
@@ -416,7 +496,7 @@ async function sendAnonymousTelemetry(toolName) {
416
496
  }
417
497
  async function handleTool(name, args) {
418
498
  // Guard: if no JWT, return actionable onboarding message instead of HTTP 401
419
- if (!getJWT()) {
499
+ if (!JWT) {
420
500
  void sendAnonymousTelemetry(name);
421
501
  // ── Zero-credential device flow ─────────────────────────────────────
422
502
  // 1st call: start device flow, return code + URL
@@ -493,14 +573,14 @@ async function handleTool(name, args) {
493
573
  // Delegate brain tools (learn, recall, session, etc.)
494
574
  const brainResult = await handleBrainTool(name, args, getConnection, apiFetch);
495
575
  if (brainResult !== null) {
496
- if (!_firstCallSuccessSent && getJWT()) {
576
+ if (!_firstCallSuccessSent && JWT) {
497
577
  _firstCallSuccessSent = true;
498
- sendFunnelEvent('first_call_success', { tool: name, instance_id: args.instance_id ?? getDefaultInstanceId() ?? '' });
578
+ sendFunnelEvent('first_call_success', { tool: name, instance_id: args.instance_id ?? _defaultInstanceId ?? '' });
499
579
  }
500
580
  // Per-tool telemetry — fires after every successful brain tool call.
501
581
  // api_key carries the cky_live_... token so the backend can resolve tenant + increment counters.
502
- const instanceId = args.instance_id ?? getDefaultInstanceId() ?? '';
503
- const telemetryExtra = getJWT() ? { api_key: getJWT(), instance_id: instanceId } : { instance_id: instanceId };
582
+ const instanceId = args.instance_id ?? _defaultInstanceId ?? '';
583
+ const telemetryExtra = JWT ? { api_key: JWT, instance_id: instanceId } : { instance_id: instanceId };
504
584
  if (name === 'recall_best_solution') {
505
585
  // recall_best_solution → increments BrainRecallCount + triggers level-up logic
506
586
  sendFunnelEvent('recall_best_solution', telemetryExtra);
@@ -528,17 +608,14 @@ async function handleTool(name, args) {
528
608
  limit: 50,
529
609
  }, getConnection, apiFetch);
530
610
  if (gitBootstrap !== null) {
531
- sendFunnelEvent('brain_from_git', telemetryExtra);
611
+ const gitCounts = _lastBrainFromGitCounts;
612
+ sendFunnelEvent('brain_from_git', { ...telemetryExtra, ...(gitCounts ?? {}) });
532
613
  sendFunnelEvent('recall_best_solution', telemetryExtra);
533
614
  // Append bootstrap summary to the session_start briefing.
534
- const bootstrapText = typeof gitBootstrap === 'object' && gitBootstrap !== null && 'content' in gitBootstrap
535
- ? gitBootstrap.content.map((c) => c.text ?? '').join('\n')
536
- : String(gitBootstrap);
537
- if (typeof brainResult === 'object' && brainResult !== null && 'content' in brainResult) {
538
- brainResult.content.push({
539
- type: 'text',
540
- text: '\n---\n' + bootstrapText,
541
- });
615
+ // brainResult is always a string here (handleBrainTool returns string for session_start).
616
+ const bootstrapText = String(gitBootstrap);
617
+ if (bootstrapText) {
618
+ return String(brainResult) + '\n---\n' + bootstrapText;
542
619
  }
543
620
  }
544
621
  }
@@ -556,7 +633,7 @@ async function handleTool(name, args) {
556
633
  const srText = typeof brainResult === 'object' && brainResult !== null && 'content' in brainResult
557
634
  ? JSON.stringify(brainResult)
558
635
  : String(brainResult ?? '');
559
- if (srText.length > 50 && !srText.includes('No lessons found') && !srText.includes('no lessons')) {
636
+ if (srText.length > 50 && !srText.includes('No lessons found') && !srText.includes('no lessons') && !srText.includes('No matches found')) {
560
637
  sendFunnelEvent('recall_best_solution', telemetryExtra);
561
638
  }
562
639
  }
@@ -565,9 +642,9 @@ async function handleTool(name, args) {
565
642
  // Delegate context tools (remember/recall/list/forget)
566
643
  const contextResult = await handleContextTool(name, args, getConnection, apiFetch);
567
644
  if (contextResult !== null) {
568
- if (!_firstCallSuccessSent && getJWT()) {
645
+ if (!_firstCallSuccessSent && JWT) {
569
646
  _firstCallSuccessSent = true;
570
- sendFunnelEvent('first_call_success', { tool: name, instance_id: args.instance_id ?? getDefaultInstanceId() ?? '' });
647
+ sendFunnelEvent('first_call_success', { tool: name, instance_id: args.instance_id ?? _defaultInstanceId ?? '' });
571
648
  }
572
649
  return contextResult;
573
650
  }
@@ -592,8 +669,8 @@ async function handleTool(name, args) {
592
669
  if (syndicateResult !== null) {
593
670
  // brain_predict is in syndicate handler — track telemetry here.
594
671
  if (name === 'brain_predict') {
595
- const instanceId = args.instance_id ?? getDefaultInstanceId() ?? '';
596
- const telemetryExtra = getJWT() ? { api_key: getJWT(), instance_id: instanceId } : { instance_id: instanceId };
672
+ const instanceId = args.instance_id ?? _defaultInstanceId ?? '';
673
+ const telemetryExtra = JWT ? { api_key: JWT, instance_id: instanceId } : { instance_id: instanceId };
597
674
  sendFunnelEvent('brain_predict', telemetryExtra);
598
675
  }
599
676
  return syndicateResult;
@@ -601,16 +678,21 @@ async function handleTool(name, args) {
601
678
  const fedbrainResult = await handleFedbrainTool(name, args, getConnection, apiFetch);
602
679
  if (fedbrainResult !== null) {
603
680
  // brain_from_git and brain_predict_failures are in fedbrain handler.
604
- const instanceId = args.instance_id ?? getDefaultInstanceId() ?? '';
605
- const telemetryExtra = getJWT() ? { api_key: getJWT(), instance_id: instanceId } : { instance_id: instanceId };
681
+ const instanceId = args.instance_id ?? _defaultInstanceId ?? '';
682
+ const telemetryExtra = JWT ? { api_key: JWT, instance_id: instanceId } : { instance_id: instanceId };
606
683
  if (name === 'brain_from_git') {
607
- sendFunnelEvent('brain_from_git', telemetryExtra);
684
+ const gitCounts = _lastBrainFromGitCounts;
685
+ sendFunnelEvent('brain_from_git', { ...telemetryExtra, ...(gitCounts ?? {}) });
608
686
  }
609
687
  else if (name === 'brain_predict_failures') {
610
688
  sendFunnelEvent('brain_predict_failures', telemetryExtra);
611
689
  }
612
690
  return fedbrainResult;
613
691
  }
692
+ // Delegate Travel Chaos Organizer tools (requires TCO_API_URL env var)
693
+ const tcoResult = await handleTcoTool(name, args, JWT ?? '');
694
+ if (tcoResult !== null)
695
+ return tcoResult;
614
696
  switch (name) {
615
697
  // ── Instance management ──────────────────────────────────────────────
616
698
  case 'get_api_status': {
@@ -630,7 +712,7 @@ async function handleTool(name, args) {
630
712
  healthStatus = `unreachable: ${e.message}`;
631
713
  }
632
714
  // Check JWT / auth
633
- if (!getJWT()) {
715
+ if (!JWT) {
634
716
  return [
635
717
  `📡 **cachly API Status**`,
636
718
  ``,
@@ -644,7 +726,7 @@ async function handleTool(name, args) {
644
726
  // Decode JWT claims (inspection only, no verification)
645
727
  let authInfo = '❌ invalid JWT format';
646
728
  try {
647
- const parts = getJWT().split('.');
729
+ const parts = JWT.split('.');
648
730
  if (parts.length === 3) {
649
731
  const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));
650
732
  const sub = payload.sub ?? '(unknown)';
@@ -676,7 +758,7 @@ async function handleTool(name, args) {
676
758
  const lines = ['\n\n🧠 **Brain Instances:**'];
677
759
  for (const inst of instances) {
678
760
  const badge = inst.status === 'running' ? '🟢' : inst.status === 'provisioning' ? '🟡' : '🔴';
679
- const defaultMark = inst.id === getDefaultInstanceId() ? ' ← active' : '';
761
+ const defaultMark = inst.id === _defaultInstanceId ? ' ← active' : '';
680
762
  lines.push(` ${badge} **${inst.name}** (\`${inst.id}\`) · ${inst.status}${defaultMark}`);
681
763
  if (inst.status === 'running' && inst.host) {
682
764
  // Try a quick ping on the cached connection
@@ -820,7 +902,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
820
902
  catch (err) {
821
903
  if (err instanceof McpError)
822
904
  throw err;
823
- throw new McpError(ErrorCode.InternalError, err.message);
905
+ const msg = err.message ?? String(err);
906
+ notify('cachly', 'tool_error', { tool: name, error: msg }).catch(() => undefined);
907
+ throw new McpError(ErrorCode.InternalError, msg);
824
908
  }
825
909
  });
826
910
  // Graceful shutdown – close all Redis connections + auto-end brain session
@@ -2030,6 +2114,7 @@ if (process.argv[2] === 'setup') {
2030
2114
  const AUTH_BASE = 'https://auth.cachly.dev/realms/cachly/protocol/openid-connect';
2031
2115
  const CLIENT_ID = 'cachly-cli';
2032
2116
  console.log('Step 1: Sign in to cachly (free, no credit card)\n');
2117
+ sendFunnelEvent('setup_auth_started');
2033
2118
  // Start device flow
2034
2119
  let deviceCode = '', userCode = '', verifyUri = '', pollInterval = 5000;
2035
2120
  try {
@@ -2055,6 +2140,7 @@ if (process.argv[2] === 'setup') {
2055
2140
  rl.close();
2056
2141
  process.exit(1);
2057
2142
  }
2143
+ sendFunnelEvent('setup_auth_completed');
2058
2144
  console.log('');
2059
2145
  deviceCode = ''; // mark as fallback so we skip polling
2060
2146
  }
@@ -2087,6 +2173,7 @@ if (process.argv[2] === 'setup') {
2087
2173
  if (tokenData.access_token) {
2088
2174
  token = tokenData.access_token;
2089
2175
  console.log(' \x1b[32m✓ Authorized!\x1b[0m\n');
2176
+ sendFunnelEvent('setup_auth_completed');
2090
2177
  break;
2091
2178
  }
2092
2179
  // authorization_pending = keep polling; slow_down = increase interval
@@ -2180,9 +2267,10 @@ if (process.argv[2] === 'setup') {
2180
2267
  // Newly created — poll until running or give up after 30 s.
2181
2268
  const newId = autoBody.instance_id;
2182
2269
  console.log(` ✓ created (${newId.slice(0, 8)}…)\n`);
2270
+ // 45 attempts × 3s = 135s — generous for free-tier in slow regions.
2183
2271
  process.stdout.write('⏳ Waiting for instance to start');
2184
- for (let attempt = 0; attempt < 15; attempt++) {
2185
- await new Promise(r => setTimeout(r, 2000));
2272
+ for (let attempt = 0; attempt < 45; attempt++) {
2273
+ await new Promise(r => setTimeout(r, 3000));
2186
2274
  process.stdout.write('.');
2187
2275
  try {
2188
2276
  const checkRes = await fetch(`${API_URL}/api/v1/instances/${newId}`, { headers: { Authorization: `Bearer ${token}` } });
@@ -2221,6 +2309,7 @@ if (process.argv[2] === 'setup') {
2221
2309
  console.log(` (Run with --instance-id <id> to use a different one)\n`);
2222
2310
  }
2223
2311
  console.log(`✓ Instance: ${instance.name} (${instance.id.slice(0, 8)}…)\n`);
2312
+ sendFunnelEvent('setup_instance_ready', { instance_id: instance.id });
2224
2313
  // ── Step 3: Detect editors ────────────────────────────────────────────────
2225
2314
  const cwd = process.cwd();
2226
2315
  const detected = [];
@@ -2469,7 +2558,7 @@ if (process.argv[2] === 'index') {
2469
2558
  const instanceId = flag('instance-id') ?? process.env.CACHLY_BRAIN_INSTANCE_ID;
2470
2559
  const maxFiles = parseInt(flag('max-files') ?? '500', 10);
2471
2560
  const namespace = flag('namespace') ?? 'cachly:sem:code';
2472
- if (!instanceId || !getJWT()) {
2561
+ if (!instanceId || !JWT) {
2473
2562
  console.error('\n❌ CACHLY_BRAIN_INSTANCE_ID and CACHLY_JWT must be set\n');
2474
2563
  console.error(' export CACHLY_BRAIN_INSTANCE_ID=<uuid>');
2475
2564
  console.error(' export CACHLY_JWT=<cky_live_...>');
@@ -2506,7 +2595,7 @@ if (process.argv[2] === 'learn-git') {
2506
2595
  const repoDir = resolve(flag('dir') ?? argv.find(a => !a.startsWith('--')) ?? '.');
2507
2596
  const instanceId = flag('instance-id') ?? process.env.CACHLY_BRAIN_INSTANCE_ID;
2508
2597
  const maxCommits = parseInt(flag('max-commits') ?? '50', 10);
2509
- if (!instanceId || !getJWT()) {
2598
+ if (!instanceId || !JWT) {
2510
2599
  console.error('\n❌ CACHLY_BRAIN_INSTANCE_ID and CACHLY_JWT must be set\n');
2511
2600
  console.error(' export CACHLY_BRAIN_INSTANCE_ID=<uuid>');
2512
2601
  console.error(' export CACHLY_JWT=<cky_live_...>');
@@ -2538,8 +2627,7 @@ if (process.argv[2] === 'learn-git') {
2538
2627
  // actionable message in their editor's MCP log instead of silent failures.
2539
2628
  // Skip for CLI commands that intentionally run without credentials.
2540
2629
  const _cliNoAuthCommands = ['demo', 'share', 'health', 'setup', 'init', 'digest', 'invite', 'badge', 'join', 'upgrade'];
2541
- // In multi-tenant HTTP mode (PORT set) we never need a startup JWT — per-request auth.
2542
- if (!JWT && !process.env.PORT && !_cliNoAuthCommands.includes(process.argv[2] ?? '') && !(!process.argv[2] && process.stdout.isTTY)) {
2630
+ if (!JWT && !_cliNoAuthCommands.includes(process.argv[2] ?? '') && !(!process.argv[2] && process.stdout.isTTY)) {
2543
2631
  // No args + no JWT in a non-TTY context = running as MCP server without credentials.
2544
2632
  // Print actionable setup banner to stdout so it's captured by callers/tests.
2545
2633
  const banner = '\n' +
@@ -2606,39 +2694,9 @@ const httpPort = process.env.PORT ? parseInt(process.env.PORT, 10) : undefined;
2606
2694
  if (httpPort) {
2607
2695
  // ── HTTP mode (Streamable HTTP transport) ───────────────────────────────
2608
2696
  // Used for Smithery URL deployment: PORT=3000 node dist/index.js
2609
- // Multi-tenant: per-request JWT + instance ID resolved via:
2610
- // 1. Authorization: Bearer <jwt> + X-Cachly-Instance-Id: <uuid>
2611
- // 2. Smithery config: ?config=base64url({CACHLY_JWT, CACHLY_BRAIN_INSTANCE_ID, ...})
2612
- // Falls back to module-level env-derived values when neither is provided.
2613
2697
  const { createServer } = await import('node:http');
2614
2698
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
2615
2699
  await server.connect(transport);
2616
- function extractReqCtx(req, url) {
2617
- let jwtVal = '';
2618
- let instId = '';
2619
- const auth = req.headers['authorization'];
2620
- if (typeof auth === 'string' && auth.toLowerCase().startsWith('bearer ')) {
2621
- jwtVal = auth.slice(7).trim();
2622
- }
2623
- const hdrInst = req.headers['x-cachly-instance-id'];
2624
- if (typeof hdrInst === 'string')
2625
- instId = hdrInst.trim();
2626
- // Smithery passes user config as a base64url-encoded JSON blob in ?config=
2627
- const cfgParam = url.searchParams.get('config');
2628
- if (cfgParam && (!jwtVal || !instId)) {
2629
- try {
2630
- const b64 = cfgParam.replace(/-/g, '+').replace(/_/g, '/');
2631
- const padded = b64 + '='.repeat((4 - b64.length % 4) % 4);
2632
- const json = JSON.parse(Buffer.from(padded, 'base64').toString('utf8'));
2633
- if (!jwtVal && typeof json.CACHLY_JWT === 'string')
2634
- jwtVal = json.CACHLY_JWT;
2635
- if (!instId && typeof json.CACHLY_BRAIN_INSTANCE_ID === 'string')
2636
- instId = json.CACHLY_BRAIN_INSTANCE_ID;
2637
- }
2638
- catch { /* malformed config — ignore, fall back to globals */ }
2639
- }
2640
- return { jwt: jwtVal, instanceId: instId };
2641
- }
2642
2700
  const httpServer = createServer((req, res) => {
2643
2701
  const url = new URL(req.url ?? '/', `http://localhost:${httpPort}`);
2644
2702
  if (url.pathname === '/health') {
@@ -2646,8 +2704,6 @@ if (httpPort) {
2646
2704
  res.end(JSON.stringify({
2647
2705
  status: 'ok',
2648
2706
  version: CURRENT_VERSION,
2649
- transport: 'streamable-http',
2650
- multiTenant: true,
2651
2707
  zeroResults: {
2652
2708
  total: zeroResultsTotal,
2653
2709
  last10: ZERO_RESULTS_LOG.slice(-10).map(e => ({ query: e.query, ts: new Date(e.ts).toISOString() })),
@@ -2656,16 +2712,7 @@ if (httpPort) {
2656
2712
  return;
2657
2713
  }
2658
2714
  if (url.pathname === '/mcp' || url.pathname === '/') {
2659
- const ctx = extractReqCtx(req, url);
2660
- reqCtx.run(ctx, () => {
2661
- transport.handleRequest(req, res).catch((err) => {
2662
- process.stderr.write(`cachly-mcp HTTP handler error: ${err.message}\n`);
2663
- if (!res.headersSent) {
2664
- res.writeHead(500, { 'Content-Type': 'application/json' });
2665
- res.end(JSON.stringify({ error: 'internal_error' }));
2666
- }
2667
- });
2668
- });
2715
+ transport.handleRequest(req, res);
2669
2716
  return;
2670
2717
  }
2671
2718
  res.writeHead(404);
@@ -2680,4 +2727,5 @@ else {
2680
2727
  const transport = new StdioServerTransport();
2681
2728
  await server.connect(transport);
2682
2729
  }
2730
+ notify('cachly', 'startup', { version: CURRENT_VERSION, mode: process.env.MCP_HTTP_PORT ? 'http' : 'stdio' }).catch(() => undefined);
2683
2731
  //# sourceMappingURL=index.js.map