slackhive 0.1.40 → 0.1.42

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 (534) hide show
  1. package/cli/dist/index.js +15786 -52
  2. package/package.json +9 -3
  3. package/.dockerignore +0 -14
  4. package/.env.example +0 -44
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -65
  6. package/.github/ISSUE_TEMPLATE/config.yml +0 -5
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -38
  8. package/.github/PULL_REQUEST_TEMPLATE.md +0 -27
  9. package/.github/dependabot.yml +0 -20
  10. package/.github/workflows/audit.yml +0 -149
  11. package/.github/workflows/ci.yml +0 -135
  12. package/CHANGELOG.md +0 -52
  13. package/CODE_OF_CONDUCT.md +0 -37
  14. package/CONTRIBUTING.md +0 -204
  15. package/SECURITY.md +0 -47
  16. package/apps/runner/Dockerfile +0 -33
  17. package/apps/runner/dist/__tests__/channel-restrictions.test.d.ts +0 -8
  18. package/apps/runner/dist/__tests__/channel-restrictions.test.js +0 -63
  19. package/apps/runner/dist/__tests__/channel-restrictions.test.js.map +0 -1
  20. package/apps/runner/dist/__tests__/claude-handler-resolve.test.d.ts +0 -20
  21. package/apps/runner/dist/__tests__/claude-handler-resolve.test.js +0 -178
  22. package/apps/runner/dist/__tests__/claude-handler-resolve.test.js.map +0 -1
  23. package/apps/runner/dist/__tests__/compile-claude-md.test.d.ts +0 -13
  24. package/apps/runner/dist/__tests__/compile-claude-md.test.js +0 -144
  25. package/apps/runner/dist/__tests__/compile-claude-md.test.js.map +0 -1
  26. package/apps/runner/dist/__tests__/memory-sync.test.d.ts +0 -11
  27. package/apps/runner/dist/__tests__/memory-sync.test.js +0 -56
  28. package/apps/runner/dist/__tests__/memory-sync.test.js.map +0 -1
  29. package/apps/runner/dist/__tests__/slack-file-support.test.d.ts +0 -9
  30. package/apps/runner/dist/__tests__/slack-file-support.test.js +0 -271
  31. package/apps/runner/dist/__tests__/slack-file-support.test.js.map +0 -1
  32. package/apps/runner/dist/__tests__/slack-formatting.test.d.ts +0 -12
  33. package/apps/runner/dist/__tests__/slack-formatting.test.js +0 -400
  34. package/apps/runner/dist/__tests__/slack-formatting.test.js.map +0 -1
  35. package/apps/runner/dist/__tests__/thread-context.test.d.ts +0 -12
  36. package/apps/runner/dist/__tests__/thread-context.test.js +0 -182
  37. package/apps/runner/dist/__tests__/thread-context.test.js.map +0 -1
  38. package/apps/runner/dist/agent-runner.d.ts +0 -118
  39. package/apps/runner/dist/agent-runner.js +0 -352
  40. package/apps/runner/dist/agent-runner.js.map +0 -1
  41. package/apps/runner/dist/claude-handler.d.ts +0 -122
  42. package/apps/runner/dist/claude-handler.js +0 -402
  43. package/apps/runner/dist/claude-handler.js.map +0 -1
  44. package/apps/runner/dist/compile-claude-md.d.ts +0 -59
  45. package/apps/runner/dist/compile-claude-md.js +0 -291
  46. package/apps/runner/dist/compile-claude-md.js.map +0 -1
  47. package/apps/runner/dist/correction-handler.d.ts +0 -46
  48. package/apps/runner/dist/correction-handler.js +0 -162
  49. package/apps/runner/dist/correction-handler.js.map +0 -1
  50. package/apps/runner/dist/correction-manager.d.ts +0 -53
  51. package/apps/runner/dist/correction-manager.js +0 -241
  52. package/apps/runner/dist/correction-manager.js.map +0 -1
  53. package/apps/runner/dist/db.d.ts +0 -193
  54. package/apps/runner/dist/db.js +0 -492
  55. package/apps/runner/dist/db.js.map +0 -1
  56. package/apps/runner/dist/index.d.ts +0 -9
  57. package/apps/runner/dist/index.js +0 -43
  58. package/apps/runner/dist/index.js.map +0 -1
  59. package/apps/runner/dist/job-scheduler.d.ts +0 -57
  60. package/apps/runner/dist/job-scheduler.js +0 -150
  61. package/apps/runner/dist/job-scheduler.js.map +0 -1
  62. package/apps/runner/dist/logger.d.ts +0 -32
  63. package/apps/runner/dist/logger.js +0 -52
  64. package/apps/runner/dist/logger.js.map +0 -1
  65. package/apps/runner/dist/mcp-process-manager.d.ts +0 -38
  66. package/apps/runner/dist/mcp-process-manager.js +0 -189
  67. package/apps/runner/dist/mcp-process-manager.js.map +0 -1
  68. package/apps/runner/dist/memory-mcp.d.ts +0 -14
  69. package/apps/runner/dist/memory-mcp.js +0 -88
  70. package/apps/runner/dist/memory-mcp.js.map +0 -1
  71. package/apps/runner/dist/memory-watcher.d.ts +0 -78
  72. package/apps/runner/dist/memory-watcher.js +0 -220
  73. package/apps/runner/dist/memory-watcher.js.map +0 -1
  74. package/apps/runner/dist/slack-handler.d.ts +0 -120
  75. package/apps/runner/dist/slack-handler.js +0 -843
  76. package/apps/runner/dist/slack-handler.js.map +0 -1
  77. package/apps/runner/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  78. package/apps/runner/package.json +0 -42
  79. package/apps/runner/src/__tests__/channel-restrictions.test.ts +0 -75
  80. package/apps/runner/src/__tests__/claude-handler-resolve.test.ts +0 -160
  81. package/apps/runner/src/__tests__/compile-claude-md.test.ts +0 -139
  82. package/apps/runner/src/__tests__/memory-sync.test.ts +0 -59
  83. package/apps/runner/src/__tests__/slack-file-support.test.ts +0 -376
  84. package/apps/runner/src/__tests__/slack-formatting.test.ts +0 -495
  85. package/apps/runner/src/__tests__/thread-context.test.ts +0 -215
  86. package/apps/runner/src/agent-runner.ts +0 -397
  87. package/apps/runner/src/claude-handler.ts +0 -475
  88. package/apps/runner/src/compile-claude-md.ts +0 -283
  89. package/apps/runner/src/correction-handler.ts +0 -191
  90. package/apps/runner/src/correction-manager.ts +0 -285
  91. package/apps/runner/src/db.ts +0 -604
  92. package/apps/runner/src/index.ts +0 -46
  93. package/apps/runner/src/job-scheduler.ts +0 -165
  94. package/apps/runner/src/logger.ts +0 -49
  95. package/apps/runner/src/mcp-process-manager.ts +0 -195
  96. package/apps/runner/src/memory-mcp.ts +0 -85
  97. package/apps/runner/src/memory-watcher.ts +0 -215
  98. package/apps/runner/src/slack-handler.ts +0 -929
  99. package/apps/runner/tsconfig.json +0 -17
  100. package/apps/runner/vitest.config.mts +0 -17
  101. package/apps/web/.eslintrc.json +0 -3
  102. package/apps/web/.next/app-build-manifest.json +0 -323
  103. package/apps/web/.next/app-path-routes-manifest.json +0 -46
  104. package/apps/web/.next/build-manifest.json +0 -33
  105. package/apps/web/.next/cache/.previewinfo +0 -1
  106. package/apps/web/.next/cache/.rscinfo +0 -1
  107. package/apps/web/.next/cache/webpack/client-production/0.pack +0 -0
  108. package/apps/web/.next/cache/webpack/client-production/1.pack +0 -0
  109. package/apps/web/.next/cache/webpack/client-production/2.pack +0 -0
  110. package/apps/web/.next/cache/webpack/client-production/3.pack +0 -0
  111. package/apps/web/.next/cache/webpack/client-production/4.pack +0 -0
  112. package/apps/web/.next/cache/webpack/client-production/index.pack +0 -0
  113. package/apps/web/.next/cache/webpack/client-production/index.pack.old +0 -0
  114. package/apps/web/.next/cache/webpack/edge-server-production/0.pack +0 -0
  115. package/apps/web/.next/cache/webpack/edge-server-production/1.pack +0 -0
  116. package/apps/web/.next/cache/webpack/edge-server-production/index.pack +0 -0
  117. package/apps/web/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  118. package/apps/web/.next/cache/webpack/server-production/0.pack +0 -0
  119. package/apps/web/.next/cache/webpack/server-production/1.pack +0 -0
  120. package/apps/web/.next/cache/webpack/server-production/2.pack +0 -0
  121. package/apps/web/.next/cache/webpack/server-production/index.pack +0 -0
  122. package/apps/web/.next/cache/webpack/server-production/index.pack.old +0 -0
  123. package/apps/web/.next/diagnostics/build-diagnostics.json +0 -6
  124. package/apps/web/.next/diagnostics/framework.json +0 -1
  125. package/apps/web/.next/package.json +0 -1
  126. package/apps/web/.next/react-loadable-manifest.json +0 -1
  127. package/apps/web/.next/server/app/_not-found/page.js +0 -2
  128. package/apps/web/.next/server/app/_not-found/page.js.nft.json +0 -1
  129. package/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  130. package/apps/web/.next/server/app/agents/[slug]/page.js +0 -4
  131. package/apps/web/.next/server/app/agents/[slug]/page.js.nft.json +0 -1
  132. package/apps/web/.next/server/app/agents/[slug]/page_client-reference-manifest.js +0 -1
  133. package/apps/web/.next/server/app/agents/new/page.js +0 -2
  134. package/apps/web/.next/server/app/agents/new/page.js.nft.json +0 -1
  135. package/apps/web/.next/server/app/agents/new/page_client-reference-manifest.js +0 -1
  136. package/apps/web/.next/server/app/api/agents/[id]/access/route.js +0 -1
  137. package/apps/web/.next/server/app/api/agents/[id]/access/route.js.nft.json +0 -1
  138. package/apps/web/.next/server/app/api/agents/[id]/access/route_client-reference-manifest.js +0 -1
  139. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js +0 -6
  140. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route.js.nft.json +0 -1
  141. package/apps/web/.next/server/app/api/agents/[id]/claude-md/route_client-reference-manifest.js +0 -1
  142. package/apps/web/.next/server/app/api/agents/[id]/logs/route.js +0 -3
  143. package/apps/web/.next/server/app/api/agents/[id]/logs/route.js.nft.json +0 -1
  144. package/apps/web/.next/server/app/api/agents/[id]/logs/route_client-reference-manifest.js +0 -1
  145. package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js +0 -1
  146. package/apps/web/.next/server/app/api/agents/[id]/manifest/route.js.nft.json +0 -1
  147. package/apps/web/.next/server/app/api/agents/[id]/manifest/route_client-reference-manifest.js +0 -1
  148. package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js +0 -1
  149. package/apps/web/.next/server/app/api/agents/[id]/mcps/route.js.nft.json +0 -1
  150. package/apps/web/.next/server/app/api/agents/[id]/mcps/route_client-reference-manifest.js +0 -1
  151. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js +0 -1
  152. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route.js.nft.json +0 -1
  153. package/apps/web/.next/server/app/api/agents/[id]/memories/[memId]/route_client-reference-manifest.js +0 -1
  154. package/apps/web/.next/server/app/api/agents/[id]/memories/route.js +0 -1
  155. package/apps/web/.next/server/app/api/agents/[id]/memories/route.js.nft.json +0 -1
  156. package/apps/web/.next/server/app/api/agents/[id]/memories/route_client-reference-manifest.js +0 -1
  157. package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js +0 -1
  158. package/apps/web/.next/server/app/api/agents/[id]/permissions/route.js.nft.json +0 -1
  159. package/apps/web/.next/server/app/api/agents/[id]/permissions/route_client-reference-manifest.js +0 -1
  160. package/apps/web/.next/server/app/api/agents/[id]/reload/route.js +0 -1
  161. package/apps/web/.next/server/app/api/agents/[id]/reload/route.js.nft.json +0 -1
  162. package/apps/web/.next/server/app/api/agents/[id]/reload/route_client-reference-manifest.js +0 -1
  163. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js +0 -1
  164. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route.js.nft.json +0 -1
  165. package/apps/web/.next/server/app/api/agents/[id]/restrictions/route_client-reference-manifest.js +0 -1
  166. package/apps/web/.next/server/app/api/agents/[id]/route.js +0 -33
  167. package/apps/web/.next/server/app/api/agents/[id]/route.js.nft.json +0 -1
  168. package/apps/web/.next/server/app/api/agents/[id]/route_client-reference-manifest.js +0 -1
  169. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js +0 -1
  170. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route.js.nft.json +0 -1
  171. package/apps/web/.next/server/app/api/agents/[id]/skills/[skillId]/route_client-reference-manifest.js +0 -1
  172. package/apps/web/.next/server/app/api/agents/[id]/skills/route.js +0 -1
  173. package/apps/web/.next/server/app/api/agents/[id]/skills/route.js.nft.json +0 -1
  174. package/apps/web/.next/server/app/api/agents/[id]/skills/route_client-reference-manifest.js +0 -1
  175. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js +0 -1
  176. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route.js.nft.json +0 -1
  177. package/apps/web/.next/server/app/api/agents/[id]/slack-info/route_client-reference-manifest.js +0 -1
  178. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js +0 -1
  179. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route.js.nft.json +0 -1
  180. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/restore/route_client-reference-manifest.js +0 -1
  181. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js +0 -1
  182. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route.js.nft.json +0 -1
  183. package/apps/web/.next/server/app/api/agents/[id]/snapshots/[sid]/route_client-reference-manifest.js +0 -1
  184. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js +0 -1
  185. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route.js.nft.json +0 -1
  186. package/apps/web/.next/server/app/api/agents/[id]/snapshots/route_client-reference-manifest.js +0 -1
  187. package/apps/web/.next/server/app/api/agents/[id]/start/route.js +0 -1
  188. package/apps/web/.next/server/app/api/agents/[id]/start/route.js.nft.json +0 -1
  189. package/apps/web/.next/server/app/api/agents/[id]/start/route_client-reference-manifest.js +0 -1
  190. package/apps/web/.next/server/app/api/agents/[id]/stop/route.js +0 -1
  191. package/apps/web/.next/server/app/api/agents/[id]/stop/route.js.nft.json +0 -1
  192. package/apps/web/.next/server/app/api/agents/[id]/stop/route_client-reference-manifest.js +0 -1
  193. package/apps/web/.next/server/app/api/agents/route.js +0 -91
  194. package/apps/web/.next/server/app/api/agents/route.js.nft.json +0 -1
  195. package/apps/web/.next/server/app/api/agents/route_client-reference-manifest.js +0 -1
  196. package/apps/web/.next/server/app/api/auth/login/route.js +0 -1
  197. package/apps/web/.next/server/app/api/auth/login/route.js.nft.json +0 -1
  198. package/apps/web/.next/server/app/api/auth/login/route_client-reference-manifest.js +0 -1
  199. package/apps/web/.next/server/app/api/auth/logout/route.js +0 -1
  200. package/apps/web/.next/server/app/api/auth/logout/route.js.nft.json +0 -1
  201. package/apps/web/.next/server/app/api/auth/logout/route_client-reference-manifest.js +0 -1
  202. package/apps/web/.next/server/app/api/auth/me/route.js +0 -1
  203. package/apps/web/.next/server/app/api/auth/me/route.js.nft.json +0 -1
  204. package/apps/web/.next/server/app/api/auth/me/route_client-reference-manifest.js +0 -1
  205. package/apps/web/.next/server/app/api/auth/users/[id]/route.js +0 -1
  206. package/apps/web/.next/server/app/api/auth/users/[id]/route.js.nft.json +0 -1
  207. package/apps/web/.next/server/app/api/auth/users/[id]/route_client-reference-manifest.js +0 -1
  208. package/apps/web/.next/server/app/api/auth/users/route.js +0 -1
  209. package/apps/web/.next/server/app/api/auth/users/route.js.nft.json +0 -1
  210. package/apps/web/.next/server/app/api/auth/users/route_client-reference-manifest.js +0 -1
  211. package/apps/web/.next/server/app/api/env-vars/[key]/route.js +0 -1
  212. package/apps/web/.next/server/app/api/env-vars/[key]/route.js.nft.json +0 -1
  213. package/apps/web/.next/server/app/api/env-vars/[key]/route_client-reference-manifest.js +0 -1
  214. package/apps/web/.next/server/app/api/env-vars/route.js +0 -1
  215. package/apps/web/.next/server/app/api/env-vars/route.js.nft.json +0 -1
  216. package/apps/web/.next/server/app/api/env-vars/route_client-reference-manifest.js +0 -1
  217. package/apps/web/.next/server/app/api/jobs/[id]/route.js +0 -1
  218. package/apps/web/.next/server/app/api/jobs/[id]/route.js.nft.json +0 -1
  219. package/apps/web/.next/server/app/api/jobs/[id]/route_client-reference-manifest.js +0 -1
  220. package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js +0 -1
  221. package/apps/web/.next/server/app/api/jobs/[id]/runs/route.js.nft.json +0 -1
  222. package/apps/web/.next/server/app/api/jobs/[id]/runs/route_client-reference-manifest.js +0 -1
  223. package/apps/web/.next/server/app/api/jobs/route.js +0 -1
  224. package/apps/web/.next/server/app/api/jobs/route.js.nft.json +0 -1
  225. package/apps/web/.next/server/app/api/jobs/route_client-reference-manifest.js +0 -1
  226. package/apps/web/.next/server/app/api/mcps/[id]/route.js +0 -1
  227. package/apps/web/.next/server/app/api/mcps/[id]/route.js.nft.json +0 -1
  228. package/apps/web/.next/server/app/api/mcps/[id]/route_client-reference-manifest.js +0 -1
  229. package/apps/web/.next/server/app/api/mcps/[id]/test/route.js +0 -1
  230. package/apps/web/.next/server/app/api/mcps/[id]/test/route.js.nft.json +0 -1
  231. package/apps/web/.next/server/app/api/mcps/[id]/test/route_client-reference-manifest.js +0 -1
  232. package/apps/web/.next/server/app/api/mcps/route.js +0 -1
  233. package/apps/web/.next/server/app/api/mcps/route.js.nft.json +0 -1
  234. package/apps/web/.next/server/app/api/mcps/route_client-reference-manifest.js +0 -1
  235. package/apps/web/.next/server/app/api/settings/route.js +0 -1
  236. package/apps/web/.next/server/app/api/settings/route.js.nft.json +0 -1
  237. package/apps/web/.next/server/app/api/settings/route_client-reference-manifest.js +0 -1
  238. package/apps/web/.next/server/app/icon.svg/route.js +0 -1
  239. package/apps/web/.next/server/app/icon.svg/route.js.nft.json +0 -1
  240. package/apps/web/.next/server/app/jobs/page.js +0 -2
  241. package/apps/web/.next/server/app/jobs/page.js.nft.json +0 -1
  242. package/apps/web/.next/server/app/jobs/page_client-reference-manifest.js +0 -1
  243. package/apps/web/.next/server/app/login/page.js +0 -2
  244. package/apps/web/.next/server/app/login/page.js.nft.json +0 -1
  245. package/apps/web/.next/server/app/login/page_client-reference-manifest.js +0 -1
  246. package/apps/web/.next/server/app/page.js +0 -2
  247. package/apps/web/.next/server/app/page.js.nft.json +0 -1
  248. package/apps/web/.next/server/app/page_client-reference-manifest.js +0 -1
  249. package/apps/web/.next/server/app/settings/env-vars/page.js +0 -2
  250. package/apps/web/.next/server/app/settings/env-vars/page.js.nft.json +0 -1
  251. package/apps/web/.next/server/app/settings/env-vars/page_client-reference-manifest.js +0 -1
  252. package/apps/web/.next/server/app/settings/mcps/page.js +0 -2
  253. package/apps/web/.next/server/app/settings/mcps/page.js.nft.json +0 -1
  254. package/apps/web/.next/server/app/settings/mcps/page_client-reference-manifest.js +0 -1
  255. package/apps/web/.next/server/app/settings/page.js +0 -2
  256. package/apps/web/.next/server/app/settings/page.js.nft.json +0 -1
  257. package/apps/web/.next/server/app/settings/page_client-reference-manifest.js +0 -1
  258. package/apps/web/.next/server/app-paths-manifest.json +0 -46
  259. package/apps/web/.next/server/chunks/1157.js +0 -9
  260. package/apps/web/.next/server/chunks/2287.js +0 -1
  261. package/apps/web/.next/server/chunks/3444.js +0 -1
  262. package/apps/web/.next/server/chunks/383.js +0 -6
  263. package/apps/web/.next/server/chunks/4012.js +0 -58
  264. package/apps/web/.next/server/chunks/6791.js +0 -1
  265. package/apps/web/.next/server/chunks/7171.js +0 -1
  266. package/apps/web/.next/server/chunks/8819.js +0 -22
  267. package/apps/web/.next/server/edge-runtime-webpack.js +0 -2
  268. package/apps/web/.next/server/edge-runtime-webpack.js.map +0 -1
  269. package/apps/web/.next/server/interception-route-rewrite-manifest.js +0 -1
  270. package/apps/web/.next/server/middleware-build-manifest.js +0 -1
  271. package/apps/web/.next/server/middleware-manifest.json +0 -32
  272. package/apps/web/.next/server/middleware-react-loadable-manifest.js +0 -1
  273. package/apps/web/.next/server/next-font-manifest.js +0 -1
  274. package/apps/web/.next/server/next-font-manifest.json +0 -1
  275. package/apps/web/.next/server/pages/_app.js +0 -1
  276. package/apps/web/.next/server/pages/_app.js.nft.json +0 -1
  277. package/apps/web/.next/server/pages/_document.js +0 -1
  278. package/apps/web/.next/server/pages/_document.js.nft.json +0 -1
  279. package/apps/web/.next/server/pages/_error.js +0 -19
  280. package/apps/web/.next/server/pages/_error.js.nft.json +0 -1
  281. package/apps/web/.next/server/pages-manifest.json +0 -5
  282. package/apps/web/.next/server/server-reference-manifest.js +0 -1
  283. package/apps/web/.next/server/server-reference-manifest.json +0 -1
  284. package/apps/web/.next/server/src/middleware.js +0 -14
  285. package/apps/web/.next/server/src/middleware.js.map +0 -1
  286. package/apps/web/.next/server/webpack-runtime.js +0 -1
  287. package/apps/web/.next/static/chunks/18-90b700ea37b686a2.js +0 -1
  288. package/apps/web/.next/static/chunks/87c73c54-24122e7b92478d00.js +0 -1
  289. package/apps/web/.next/static/chunks/9664-af80478aa73ba424.js +0 -1
  290. package/apps/web/.next/static/chunks/app/_not-found/page-b9cee17ed89ca24a.js +0 -1
  291. package/apps/web/.next/static/chunks/app/agents/[slug]/page-18369fc3fe1a9a7b.js +0 -1
  292. package/apps/web/.next/static/chunks/app/agents/new/page-bf11cf8901c7e2cd.js +0 -1
  293. package/apps/web/.next/static/chunks/app/api/agents/[id]/access/route-07f0f73ac9839899.js +0 -1
  294. package/apps/web/.next/static/chunks/app/api/agents/[id]/claude-md/route-07f0f73ac9839899.js +0 -1
  295. package/apps/web/.next/static/chunks/app/api/agents/[id]/logs/route-07f0f73ac9839899.js +0 -1
  296. package/apps/web/.next/static/chunks/app/api/agents/[id]/manifest/route-07f0f73ac9839899.js +0 -1
  297. package/apps/web/.next/static/chunks/app/api/agents/[id]/mcps/route-07f0f73ac9839899.js +0 -1
  298. package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/[memId]/route-07f0f73ac9839899.js +0 -1
  299. package/apps/web/.next/static/chunks/app/api/agents/[id]/memories/route-07f0f73ac9839899.js +0 -1
  300. package/apps/web/.next/static/chunks/app/api/agents/[id]/permissions/route-07f0f73ac9839899.js +0 -1
  301. package/apps/web/.next/static/chunks/app/api/agents/[id]/reload/route-07f0f73ac9839899.js +0 -1
  302. package/apps/web/.next/static/chunks/app/api/agents/[id]/restrictions/route-07f0f73ac9839899.js +0 -1
  303. package/apps/web/.next/static/chunks/app/api/agents/[id]/route-07f0f73ac9839899.js +0 -1
  304. package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/[skillId]/route-07f0f73ac9839899.js +0 -1
  305. package/apps/web/.next/static/chunks/app/api/agents/[id]/skills/route-07f0f73ac9839899.js +0 -1
  306. package/apps/web/.next/static/chunks/app/api/agents/[id]/slack-info/route-07f0f73ac9839899.js +0 -1
  307. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/restore/route-07f0f73ac9839899.js +0 -1
  308. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/[sid]/route-07f0f73ac9839899.js +0 -1
  309. package/apps/web/.next/static/chunks/app/api/agents/[id]/snapshots/route-07f0f73ac9839899.js +0 -1
  310. package/apps/web/.next/static/chunks/app/api/agents/[id]/start/route-07f0f73ac9839899.js +0 -1
  311. package/apps/web/.next/static/chunks/app/api/agents/[id]/stop/route-07f0f73ac9839899.js +0 -1
  312. package/apps/web/.next/static/chunks/app/api/agents/route-07f0f73ac9839899.js +0 -1
  313. package/apps/web/.next/static/chunks/app/api/auth/login/route-07f0f73ac9839899.js +0 -1
  314. package/apps/web/.next/static/chunks/app/api/auth/logout/route-07f0f73ac9839899.js +0 -1
  315. package/apps/web/.next/static/chunks/app/api/auth/me/route-07f0f73ac9839899.js +0 -1
  316. package/apps/web/.next/static/chunks/app/api/auth/users/[id]/route-07f0f73ac9839899.js +0 -1
  317. package/apps/web/.next/static/chunks/app/api/auth/users/route-07f0f73ac9839899.js +0 -1
  318. package/apps/web/.next/static/chunks/app/api/env-vars/[key]/route-07f0f73ac9839899.js +0 -1
  319. package/apps/web/.next/static/chunks/app/api/env-vars/route-07f0f73ac9839899.js +0 -1
  320. package/apps/web/.next/static/chunks/app/api/jobs/[id]/route-07f0f73ac9839899.js +0 -1
  321. package/apps/web/.next/static/chunks/app/api/jobs/[id]/runs/route-07f0f73ac9839899.js +0 -1
  322. package/apps/web/.next/static/chunks/app/api/jobs/route-07f0f73ac9839899.js +0 -1
  323. package/apps/web/.next/static/chunks/app/api/mcps/[id]/route-07f0f73ac9839899.js +0 -1
  324. package/apps/web/.next/static/chunks/app/api/mcps/[id]/test/route-07f0f73ac9839899.js +0 -1
  325. package/apps/web/.next/static/chunks/app/api/mcps/route-07f0f73ac9839899.js +0 -1
  326. package/apps/web/.next/static/chunks/app/api/settings/route-07f0f73ac9839899.js +0 -1
  327. package/apps/web/.next/static/chunks/app/jobs/page-f5aa89a47c50efd8.js +0 -1
  328. package/apps/web/.next/static/chunks/app/layout-2079f4964aa7314e.js +0 -1
  329. package/apps/web/.next/static/chunks/app/login/layout-07f0f73ac9839899.js +0 -1
  330. package/apps/web/.next/static/chunks/app/login/page-aa259283dc38e8f9.js +0 -1
  331. package/apps/web/.next/static/chunks/app/page-e83437b608104dff.js +0 -1
  332. package/apps/web/.next/static/chunks/app/settings/env-vars/page-06479dbdfb78b76b.js +0 -1
  333. package/apps/web/.next/static/chunks/app/settings/mcps/page-75650686ed6490c7.js +0 -1
  334. package/apps/web/.next/static/chunks/app/settings/page-e1e62fc41ff6cddd.js +0 -1
  335. package/apps/web/.next/static/chunks/framework-811407f832a33072.js +0 -1
  336. package/apps/web/.next/static/chunks/main-3f1cddbdd67b1546.js +0 -1
  337. package/apps/web/.next/static/chunks/main-app-cebd8a6a5ccbf72d.js +0 -1
  338. package/apps/web/.next/static/chunks/pages/_app-50fa07b56b2d29ac.js +0 -1
  339. package/apps/web/.next/static/chunks/pages/_error-fed8688bdd23f211.js +0 -1
  340. package/apps/web/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  341. package/apps/web/.next/static/chunks/webpack-6c05566dba553c97.js +0 -1
  342. package/apps/web/.next/static/css/15371687405525e2.css +0 -5
  343. package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_buildManifest.js +0 -1
  344. package/apps/web/.next/static/ikfNbLhuw7jntn35bz0lk/_ssgManifest.js +0 -1
  345. package/apps/web/.next/trace +0 -5
  346. package/apps/web/.next/types/app/agents/[slug]/page.ts +0 -84
  347. package/apps/web/.next/types/app/agents/new/page.ts +0 -84
  348. package/apps/web/.next/types/app/api/agents/[id]/access/route.ts +0 -347
  349. package/apps/web/.next/types/app/api/agents/[id]/claude-md/route.ts +0 -347
  350. package/apps/web/.next/types/app/api/agents/[id]/logs/route.ts +0 -347
  351. package/apps/web/.next/types/app/api/agents/[id]/manifest/route.ts +0 -347
  352. package/apps/web/.next/types/app/api/agents/[id]/mcps/route.ts +0 -347
  353. package/apps/web/.next/types/app/api/agents/[id]/memories/[memId]/route.ts +0 -347
  354. package/apps/web/.next/types/app/api/agents/[id]/memories/route.ts +0 -347
  355. package/apps/web/.next/types/app/api/agents/[id]/permissions/route.ts +0 -347
  356. package/apps/web/.next/types/app/api/agents/[id]/reload/route.ts +0 -347
  357. package/apps/web/.next/types/app/api/agents/[id]/restrictions/route.ts +0 -347
  358. package/apps/web/.next/types/app/api/agents/[id]/route.ts +0 -347
  359. package/apps/web/.next/types/app/api/agents/[id]/skills/[skillId]/route.ts +0 -347
  360. package/apps/web/.next/types/app/api/agents/[id]/skills/route.ts +0 -347
  361. package/apps/web/.next/types/app/api/agents/[id]/slack-info/route.ts +0 -347
  362. package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +0 -347
  363. package/apps/web/.next/types/app/api/agents/[id]/snapshots/[sid]/route.ts +0 -347
  364. package/apps/web/.next/types/app/api/agents/[id]/snapshots/route.ts +0 -347
  365. package/apps/web/.next/types/app/api/agents/[id]/start/route.ts +0 -347
  366. package/apps/web/.next/types/app/api/agents/[id]/stop/route.ts +0 -347
  367. package/apps/web/.next/types/app/api/agents/route.ts +0 -347
  368. package/apps/web/.next/types/app/api/auth/login/route.ts +0 -347
  369. package/apps/web/.next/types/app/api/auth/logout/route.ts +0 -347
  370. package/apps/web/.next/types/app/api/auth/me/route.ts +0 -347
  371. package/apps/web/.next/types/app/api/auth/users/[id]/route.ts +0 -347
  372. package/apps/web/.next/types/app/api/auth/users/route.ts +0 -347
  373. package/apps/web/.next/types/app/api/env-vars/[key]/route.ts +0 -347
  374. package/apps/web/.next/types/app/api/env-vars/route.ts +0 -347
  375. package/apps/web/.next/types/app/api/jobs/[id]/route.ts +0 -347
  376. package/apps/web/.next/types/app/api/jobs/[id]/runs/route.ts +0 -347
  377. package/apps/web/.next/types/app/api/jobs/route.ts +0 -347
  378. package/apps/web/.next/types/app/api/mcps/[id]/route.ts +0 -347
  379. package/apps/web/.next/types/app/api/mcps/[id]/test/route.ts +0 -347
  380. package/apps/web/.next/types/app/api/mcps/route.ts +0 -347
  381. package/apps/web/.next/types/app/api/settings/route.ts +0 -347
  382. package/apps/web/.next/types/app/jobs/page.ts +0 -84
  383. package/apps/web/.next/types/app/login/layout.ts +0 -84
  384. package/apps/web/.next/types/app/login/page.ts +0 -84
  385. package/apps/web/.next/types/app/page.ts +0 -84
  386. package/apps/web/.next/types/app/settings/env-vars/page.ts +0 -84
  387. package/apps/web/.next/types/app/settings/mcps/page.ts +0 -84
  388. package/apps/web/.next/types/app/settings/page.ts +0 -84
  389. package/apps/web/.next/types/cache-life.d.ts +0 -141
  390. package/apps/web/.next/types/package.json +0 -1
  391. package/apps/web/.next/types/routes.d.ts +0 -114
  392. package/apps/web/.next/types/validator.ts +0 -448
  393. package/apps/web/Dockerfile +0 -37
  394. package/apps/web/next-env.d.ts +0 -6
  395. package/apps/web/next.config.js +0 -6
  396. package/apps/web/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  397. package/apps/web/package.json +0 -48
  398. package/apps/web/postcss.config.js +0 -3
  399. package/apps/web/public/logo.svg +0 -17
  400. package/apps/web/src/app/agents/[slug]/page.tsx +0 -2235
  401. package/apps/web/src/app/agents/new/page.tsx +0 -1161
  402. package/apps/web/src/app/api/agents/[id]/access/route.ts +0 -76
  403. package/apps/web/src/app/api/agents/[id]/claude-md/route.ts +0 -111
  404. package/apps/web/src/app/api/agents/[id]/logs/route.ts +0 -84
  405. package/apps/web/src/app/api/agents/[id]/manifest/route.ts +0 -32
  406. package/apps/web/src/app/api/agents/[id]/mcps/route.ts +0 -73
  407. package/apps/web/src/app/api/agents/[id]/memories/[memId]/route.ts +0 -31
  408. package/apps/web/src/app/api/agents/[id]/memories/route.ts +0 -56
  409. package/apps/web/src/app/api/agents/[id]/permissions/route.ts +0 -74
  410. package/apps/web/src/app/api/agents/[id]/reload/route.ts +0 -33
  411. package/apps/web/src/app/api/agents/[id]/restrictions/route.ts +0 -85
  412. package/apps/web/src/app/api/agents/[id]/route.ts +0 -81
  413. package/apps/web/src/app/api/agents/[id]/skills/[skillId]/route.ts +0 -52
  414. package/apps/web/src/app/api/agents/[id]/skills/route.ts +0 -80
  415. package/apps/web/src/app/api/agents/[id]/slack-info/route.ts +0 -38
  416. package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/restore/route.ts +0 -61
  417. package/apps/web/src/app/api/agents/[id]/snapshots/[sid]/route.ts +0 -53
  418. package/apps/web/src/app/api/agents/[id]/snapshots/route.ts +0 -84
  419. package/apps/web/src/app/api/agents/[id]/start/route.ts +0 -35
  420. package/apps/web/src/app/api/agents/[id]/stop/route.ts +0 -35
  421. package/apps/web/src/app/api/agents/route.ts +0 -99
  422. package/apps/web/src/app/api/auth/login/route.ts +0 -39
  423. package/apps/web/src/app/api/auth/logout/route.ts +0 -21
  424. package/apps/web/src/app/api/auth/me/route.ts +0 -24
  425. package/apps/web/src/app/api/auth/users/[id]/route.ts +0 -48
  426. package/apps/web/src/app/api/auth/users/route.ts +0 -63
  427. package/apps/web/src/app/api/env-vars/[key]/route.ts +0 -66
  428. package/apps/web/src/app/api/env-vars/route.ts +0 -59
  429. package/apps/web/src/app/api/jobs/[id]/route.ts +0 -51
  430. package/apps/web/src/app/api/jobs/[id]/runs/route.ts +0 -24
  431. package/apps/web/src/app/api/jobs/route.ts +0 -42
  432. package/apps/web/src/app/api/mcps/[id]/route.ts +0 -60
  433. package/apps/web/src/app/api/mcps/[id]/test/route.ts +0 -195
  434. package/apps/web/src/app/api/mcps/route.ts +0 -72
  435. package/apps/web/src/app/api/settings/route.ts +0 -42
  436. package/apps/web/src/app/globals.css +0 -124
  437. package/apps/web/src/app/icon.svg +0 -17
  438. package/apps/web/src/app/jobs/page.tsx +0 -543
  439. package/apps/web/src/app/layout-shell.tsx +0 -89
  440. package/apps/web/src/app/layout.tsx +0 -18
  441. package/apps/web/src/app/login/layout.tsx +0 -9
  442. package/apps/web/src/app/login/page.tsx +0 -150
  443. package/apps/web/src/app/page.tsx +0 -573
  444. package/apps/web/src/app/settings/env-vars/page.tsx +0 -216
  445. package/apps/web/src/app/settings/mcps/page.tsx +0 -763
  446. package/apps/web/src/app/settings/page.tsx +0 -528
  447. package/apps/web/src/app/sidebar.tsx +0 -345
  448. package/apps/web/src/lib/__tests__/api-guard.test.ts +0 -189
  449. package/apps/web/src/lib/__tests__/auth.test.ts +0 -262
  450. package/apps/web/src/lib/__tests__/boss-registry.test.ts +0 -323
  451. package/apps/web/src/lib/__tests__/compile.test.ts +0 -161
  452. package/apps/web/src/lib/__tests__/db-agent-hierarchy.test.ts +0 -136
  453. package/apps/web/src/lib/__tests__/db-env-vars.test.ts +0 -216
  454. package/apps/web/src/lib/__tests__/db-restrictions.test.ts +0 -117
  455. package/apps/web/src/lib/__tests__/db.integration.test.ts +0 -271
  456. package/apps/web/src/lib/__tests__/diff.test.ts +0 -102
  457. package/apps/web/src/lib/__tests__/mcp-mask.test.ts +0 -274
  458. package/apps/web/src/lib/__tests__/skill-templates.test.ts +0 -237
  459. package/apps/web/src/lib/__tests__/slack-manifest.test.ts +0 -105
  460. package/apps/web/src/lib/api-guard.ts +0 -68
  461. package/apps/web/src/lib/auth-context.tsx +0 -71
  462. package/apps/web/src/lib/auth.ts +0 -128
  463. package/apps/web/src/lib/boss-registry.ts +0 -90
  464. package/apps/web/src/lib/compile.ts +0 -51
  465. package/apps/web/src/lib/db.ts +0 -1196
  466. package/apps/web/src/lib/diff.ts +0 -43
  467. package/apps/web/src/lib/mcp-mask.ts +0 -91
  468. package/apps/web/src/lib/portal.tsx +0 -23
  469. package/apps/web/src/lib/skill-templates.ts +0 -148
  470. package/apps/web/src/lib/slack-manifest.ts +0 -85
  471. package/apps/web/src/middleware.ts +0 -68
  472. package/apps/web/tailwind.config.js +0 -6
  473. package/apps/web/tsconfig.json +0 -23
  474. package/apps/web/vitest.config.mts +0 -21
  475. package/cli/.claude/settings.local.json +0 -6
  476. package/cli/node_modules/.package-lock.json +0 -427
  477. package/cli/node_modules/commander/LICENSE +0 -22
  478. package/cli/node_modules/commander/Readme.md +0 -1157
  479. package/cli/node_modules/commander/esm.mjs +0 -16
  480. package/cli/node_modules/commander/index.js +0 -24
  481. package/cli/node_modules/commander/lib/argument.js +0 -149
  482. package/cli/node_modules/commander/lib/command.js +0 -2509
  483. package/cli/node_modules/commander/lib/error.js +0 -39
  484. package/cli/node_modules/commander/lib/help.js +0 -520
  485. package/cli/node_modules/commander/lib/option.js +0 -330
  486. package/cli/node_modules/commander/lib/suggestSimilar.js +0 -101
  487. package/cli/node_modules/commander/package-support.json +0 -16
  488. package/cli/node_modules/commander/package.json +0 -84
  489. package/cli/node_modules/commander/typings/esm.d.mts +0 -3
  490. package/cli/node_modules/commander/typings/index.d.ts +0 -969
  491. package/cli/package-lock.json +0 -449
  492. package/cli/package.json +0 -44
  493. package/cli/src/commands/init.ts +0 -514
  494. package/cli/src/commands/manage.ts +0 -115
  495. package/cli/src/index.ts +0 -63
  496. package/cli/tsconfig.json +0 -14
  497. package/docker-compose.yml +0 -122
  498. package/docs/agents/boss-agents.mdx +0 -108
  499. package/docs/agents/creating-agents.mdx +0 -132
  500. package/docs/agents/memory.mdx +0 -113
  501. package/docs/agents/tools.mdx +0 -103
  502. package/docs/configuration/env-vars.mdx +0 -166
  503. package/docs/configuration/mcp-servers.mdx +0 -203
  504. package/docs/configuration/slack-app.mdx +0 -175
  505. package/docs/docs.json +0 -79
  506. package/docs/favicon.svg +0 -17
  507. package/docs/features/history.mdx +0 -60
  508. package/docs/features/import-export.mdx +0 -77
  509. package/docs/features/logs.mdx +0 -131
  510. package/docs/features/multi-workspace.mdx +0 -90
  511. package/docs/features/scheduled-jobs.mdx +0 -231
  512. package/docs/features/users.mdx +0 -92
  513. package/docs/introduction.mdx +0 -160
  514. package/docs/logo/dark.svg +0 -17
  515. package/docs/logo/light.svg +0 -17
  516. package/docs/logo/wide-dark.svg +0 -12
  517. package/docs/logo/wide-light.svg +0 -12
  518. package/docs/quickstart.mdx +0 -270
  519. package/docs/self-hosting/docker.mdx +0 -151
  520. package/docs/self-hosting/production.mdx +0 -176
  521. package/packages/shared/dist/index.d.ts +0 -8
  522. package/packages/shared/dist/index.d.ts.map +0 -1
  523. package/packages/shared/dist/index.js +0 -24
  524. package/packages/shared/dist/index.js.map +0 -1
  525. package/packages/shared/dist/types.d.ts +0 -584
  526. package/packages/shared/dist/types.d.ts.map +0 -1
  527. package/packages/shared/dist/types.js +0 -39
  528. package/packages/shared/dist/types.js.map +0 -1
  529. package/packages/shared/package.json +0 -15
  530. package/packages/shared/src/db/schema.sql +0 -354
  531. package/packages/shared/src/index.ts +0 -8
  532. package/packages/shared/src/types.ts +0 -683
  533. package/packages/shared/tsconfig.json +0 -17
  534. package/scripts/dev.sh +0 -45
@@ -1,763 +0,0 @@
1
- 'use client';
2
-
3
- /**
4
- * @fileoverview Settings → MCP Catalog page.
5
- * Global MCP server catalog — add, edit, enable/disable, delete, test.
6
- * Supports stdio, SSE, HTTP, and inline TypeScript transports.
7
- *
8
- * @module web/settings/mcps/page
9
- */
10
-
11
- import { useState, useEffect } from 'react';
12
- import type { McpServer, McpServerType } from '@slackhive/shared';
13
- import { useAuth } from '@/lib/auth-context';
14
- import { Settings } from 'lucide-react';
15
-
16
- // ─── Types ───────────────────────────────────────────────────────────────────
17
-
18
- /** UI transport type — 'typescript' is sent to the API as 'stdio' with tsSource */
19
- type UiTransportType = McpServerType | 'typescript';
20
-
21
- interface EnvEntry {
22
- key: string;
23
- mode: 'value' | 'ref';
24
- val: string; // raw value (mode=value) or env_vars key name (mode=ref)
25
- }
26
-
27
- /** A single HTTP header row — value is either a static string or pulled from an env var */
28
- interface HeaderEntry {
29
- name: string;
30
- mode: 'value' | 'ref';
31
- val: string; // static value (mode=value) or env_vars key name (mode=ref)
32
- prefix: string; // prepended to env var value, e.g. "Bearer " (mode=ref only)
33
- }
34
-
35
- interface McpFormState {
36
- name: string;
37
- uiType: UiTransportType;
38
- description: string;
39
- enabled: boolean;
40
- // stdio fields
41
- command: string;
42
- args: string;
43
- envEntries: EnvEntry[];
44
- // typescript field
45
- tsSource: string;
46
- // sse/http fields
47
- url: string;
48
- headerEntries: HeaderEntry[];
49
- }
50
-
51
- const DEFAULT_FORM: McpFormState = {
52
- name: '', uiType: 'stdio', description: '', enabled: true,
53
- command: '', args: '', envEntries: [],
54
- tsSource: '// MCP server TypeScript source\n// See: https://modelcontextprotocol.io/docs\n',
55
- url: '', headerEntries: [] as HeaderEntry[],
56
- };
57
-
58
- // ─── Page ────────────────────────────────────────────────────────────────────
59
-
60
- export default function McpSettingsPage() {
61
- const { canEdit } = useAuth();
62
- const [servers, setServers] = useState<McpServer[]>([]);
63
- const [loading, setLoading] = useState(true);
64
- const [form, setForm] = useState<McpFormState>(DEFAULT_FORM);
65
- const [saving, setSaving] = useState(false);
66
- const [error, setError] = useState('');
67
- const [editingId, setEditingId] = useState<string | null>(null);
68
- const [showForm, setShowForm] = useState(false);
69
- const [envVarKeys, setEnvVarKeys] = useState<string[]>([]);
70
- const [testResults, setTestResults] = useState<Record<string, { ok: boolean; message?: string; error?: string } | 'testing'>>({});
71
-
72
- useEffect(() => { load(); }, []);
73
- useEffect(() => {
74
- // Load available env var keys for the ref dropdown
75
- fetch('/api/env-vars').then(r => r.json()).then((rows: { key: string }[]) => setEnvVarKeys(rows.map(r => r.key))).catch(() => {});
76
- }, []);
77
-
78
- const load = async () => {
79
- setLoading(true);
80
- try {
81
- const r = await fetch('/api/mcps');
82
- setServers(await r.json());
83
- } finally { setLoading(false); }
84
- };
85
-
86
- // ─── Config builder ─────────────────────────────────────────────────────────
87
-
88
- const buildConfig = (f: McpFormState): object => {
89
- if (f.uiType === 'typescript') {
90
- const env = entriesToEnv(f.envEntries);
91
- const envRefs = entriesToRefs(f.envEntries);
92
- const cfg: Record<string, unknown> = { command: 'tsx', tsSource: f.tsSource };
93
- if (Object.keys(env).length > 0) cfg.env = env;
94
- if (Object.keys(envRefs).length > 0) cfg.envRefs = envRefs;
95
- return cfg;
96
- }
97
- if (f.uiType === 'stdio') {
98
- const cfg: Record<string, unknown> = { command: f.command };
99
- if (f.args.trim()) cfg.args = f.args.split(',').map(a => a.trim()).filter(Boolean);
100
- const env = entriesToEnv(f.envEntries);
101
- const envRefs = entriesToRefs(f.envEntries);
102
- if (Object.keys(env).length > 0) cfg.env = env;
103
- if (Object.keys(envRefs).length > 0) cfg.envRefs = envRefs;
104
- return cfg;
105
- }
106
- // sse / http
107
- const cfg: Record<string, unknown> = { url: f.url };
108
- const headers: Record<string, string> = {};
109
- const envRefs: Record<string, string> = {};
110
- for (const h of f.headerEntries) {
111
- if (!h.name) continue;
112
- if (h.mode === 'value') {
113
- headers[h.name] = h.val;
114
- } else {
115
- headers[h.name] = h.prefix; // e.g. "Bearer " — runner prepends this to env var value
116
- envRefs[h.name] = h.val;
117
- }
118
- }
119
- if (Object.keys(headers).length > 0) cfg.headers = headers;
120
- if (Object.keys(envRefs).length > 0) cfg.envRefs = envRefs;
121
- return cfg;
122
- };
123
-
124
- const entriesToEnv = (entries: EnvEntry[]) =>
125
- Object.fromEntries(entries.filter(e => e.mode === 'value' && e.key).map(e => [e.key, e.val]));
126
-
127
- const entriesToRefs = (entries: EnvEntry[]) =>
128
- Object.fromEntries(entries.filter(e => e.mode === 'ref' && e.key && e.val).map(e => [e.key, e.val]));
129
-
130
- // API type is always 'stdio' for typescript
131
- const apiType = (uiType: UiTransportType): McpServerType =>
132
- uiType === 'typescript' ? 'stdio' : uiType;
133
-
134
- // ─── Handlers ───────────────────────────────────────────────────────────────
135
-
136
- const handleSubmit = async (e: React.FormEvent) => {
137
- e.preventDefault();
138
- setSaving(true); setError('');
139
- try {
140
- const r = await fetch(editingId ? `/api/mcps/${editingId}` : '/api/mcps', {
141
- method: editingId ? 'PATCH' : 'POST',
142
- headers: { 'Content-Type': 'application/json' },
143
- body: JSON.stringify({
144
- name: form.name,
145
- type: apiType(form.uiType),
146
- description: form.description || undefined,
147
- enabled: form.enabled,
148
- config: buildConfig(form),
149
- }),
150
- });
151
- if (!r.ok) { const b = await r.json(); throw new Error(b.error ?? 'Failed'); }
152
- resetForm(); await load();
153
- } catch (err) { setError((err as Error).message); }
154
- finally { setSaving(false); }
155
- };
156
-
157
- const handleDelete = async (id: string) => {
158
- if (!confirm('Remove this MCP server from the catalog?')) return;
159
- await fetch(`/api/mcps/${id}`, { method: 'DELETE' });
160
- load();
161
- };
162
-
163
- const handleToggle = async (server: McpServer) => {
164
- await fetch(`/api/mcps/${server.id}`, {
165
- method: 'PATCH', headers: { 'Content-Type': 'application/json' },
166
- body: JSON.stringify({ enabled: !server.enabled }),
167
- });
168
- load();
169
- };
170
-
171
- const handleTest = async (server: McpServer) => {
172
- setTestResults(prev => ({ ...prev, [server.id]: 'testing' }));
173
- try {
174
- const r = await fetch(`/api/mcps/${server.id}/test`, { method: 'POST' });
175
- const result = await r.json() as { ok: boolean; message?: string; error?: string };
176
- setTestResults(prev => ({ ...prev, [server.id]: result }));
177
- } catch {
178
- setTestResults(prev => ({ ...prev, [server.id]: { ok: false, error: 'Request failed' } }));
179
- }
180
- };
181
-
182
- const handleEdit = (server: McpServer) => {
183
- const cfg = server.config as unknown as Record<string, unknown>;
184
- const isTs = typeof cfg.tsSource === 'string';
185
- const envObj = (cfg.env as Record<string, string>) ?? {};
186
- const envRefsObj = (cfg.envRefs as Record<string, string>) ?? {};
187
-
188
- const envEntries: EnvEntry[] = [
189
- ...Object.entries(envObj).map(([k, v]) => ({ key: k, mode: 'value' as const, val: v })),
190
- ...Object.entries(envRefsObj).map(([k, v]) => ({ key: k, mode: 'ref' as const, val: v })),
191
- ];
192
-
193
- const headersObj = (cfg.headers as Record<string, string>) ?? {};
194
- const envRefsObj2 = (cfg.envRefs as Record<string, string>) ?? {};
195
- const headerEntries: HeaderEntry[] = Object.entries(headersObj).map(([name, rawVal]) => {
196
- if (envRefsObj2[name] !== undefined) {
197
- return { name, mode: 'ref' as const, val: envRefsObj2[name], prefix: rawVal };
198
- }
199
- return { name, mode: 'value' as const, val: rawVal, prefix: '' };
200
- });
201
-
202
- setForm({
203
- name: server.name,
204
- uiType: isTs ? 'typescript' : server.type,
205
- description: server.description ?? '',
206
- enabled: server.enabled,
207
- command: (cfg.command as string) ?? '',
208
- args: Array.isArray(cfg.args) ? (cfg.args as string[]).join(', ') : '',
209
- envEntries,
210
- tsSource: isTs ? (cfg.tsSource as string) : DEFAULT_FORM.tsSource,
211
- url: (cfg.url as string) ?? '',
212
- headerEntries,
213
- });
214
- setEditingId(server.id);
215
- setShowForm(true);
216
- };
217
-
218
- const resetForm = () => { setForm(DEFAULT_FORM); setEditingId(null); setShowForm(false); setError(''); };
219
-
220
- const f = (key: keyof McpFormState, val: unknown) => setForm(prev => ({ ...prev, [key]: val }));
221
-
222
- // ─── Env entry helpers ──────────────────────────────────────────────────────
223
-
224
- const addEnvEntry = () => setForm(prev => ({ ...prev, envEntries: [...prev.envEntries, { key: '', mode: 'value', val: '' }] }));
225
- const removeEnvEntry = (i: number) => setForm(prev => ({ ...prev, envEntries: prev.envEntries.filter((_, idx) => idx !== i) }));
226
- const updateEnvEntry = (i: number, patch: Partial<EnvEntry>) =>
227
- setForm(prev => ({ ...prev, envEntries: prev.envEntries.map((e, idx) => idx === i ? { ...e, ...patch } : e) }));
228
-
229
- const addHeaderEntry = () => setForm(prev => ({ ...prev, headerEntries: [...prev.headerEntries, { name: '', mode: 'value', val: '', prefix: '' }] }));
230
- const removeHeaderEntry = (i: number) => setForm(prev => ({ ...prev, headerEntries: prev.headerEntries.filter((_, idx) => idx !== i) }));
231
- const updateHeaderEntry = (i: number, patch: Partial<HeaderEntry>) =>
232
- setForm(prev => ({ ...prev, headerEntries: prev.headerEntries.map((e, idx) => idx === i ? { ...e, ...patch } : e) }));
233
-
234
- // ─── Render ──────────────────────────────────────────────────────────────────
235
-
236
- return (
237
- <div style={{ padding: '32px 36px', maxWidth: 860 }} className="fade-up">
238
- {/* Header */}
239
- <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 28 }}>
240
- <div>
241
- <div style={{ fontSize: 11.5, color: 'var(--muted)', marginBottom: 4 }}>Settings</div>
242
- <h1 style={{ margin: 0, fontSize: 22, fontWeight: 600, letterSpacing: '-0.02em', color: 'var(--text)' }}>
243
- MCP Catalog
244
- </h1>
245
- <p style={{ margin: '4px 0 0', fontSize: 13, color: 'var(--muted)' }}>
246
- {servers.length} server{servers.length !== 1 ? 's' : ''} · available to all agents
247
- </p>
248
- </div>
249
- {canEdit && !showForm && (
250
- <button onClick={() => setShowForm(true)} style={{
251
- display: 'inline-flex', alignItems: 'center', gap: 6,
252
- background: 'var(--accent)', color: '#fff',
253
- padding: '8px 16px', borderRadius: 8, border: 'none',
254
- fontSize: 13, fontWeight: 500, cursor: 'pointer', fontFamily: 'var(--font-sans)',
255
- transition: 'opacity 0.15s',
256
- }}
257
- onMouseEnter={e => (e.currentTarget.style.opacity = '0.85')}
258
- onMouseLeave={e => (e.currentTarget.style.opacity = '1')}
259
- >
260
- <span style={{ fontSize: 16, lineHeight: 1, marginTop: -1 }}>+</span>
261
- Add Server
262
- </button>
263
- )}
264
- </div>
265
-
266
- {/* Server list */}
267
- {loading ? (
268
- <div style={{ color: 'var(--muted)', fontSize: 13 }}>Loading…</div>
269
- ) : servers.length === 0 && !showForm ? (
270
- <div style={{
271
- border: '1px dashed var(--border)', borderRadius: 12, padding: '48px',
272
- textAlign: 'center', color: 'var(--subtle)',
273
- }}>
274
- <div style={{ marginBottom: 10, color: 'var(--subtle)' }}><Settings size={28} /></div>
275
- <p style={{ margin: '0 0 4px', fontSize: 15, fontWeight: 500, color: 'var(--muted)' }}>No MCP servers yet</p>
276
- <p style={{ margin: '0 0 16px', fontSize: 13 }}>Add servers to the catalog to enable agent tools.</p>
277
- {canEdit && <button onClick={() => setShowForm(true)} style={{
278
- background: 'var(--accent)', color: '#fff', border: 'none', borderRadius: 8,
279
- padding: '8px 20px', fontSize: 13, fontWeight: 500, cursor: 'pointer',
280
- fontFamily: 'var(--font-sans)',
281
- }}>Add First Server</button>}
282
- </div>
283
- ) : (
284
- <div style={{ border: '1px solid var(--border)', borderRadius: 12, overflow: 'hidden', marginBottom: 20 }}>
285
- {servers.map((server, i) => (
286
- <ServerRow
287
- key={server.id} server={server}
288
- isLast={i === servers.length - 1}
289
- onEdit={() => handleEdit(server)}
290
- onDelete={() => handleDelete(server.id)}
291
- onToggle={() => handleToggle(server)}
292
- onTest={() => handleTest(server)}
293
- testResult={testResults[server.id]}
294
- canEdit={canEdit}
295
- />
296
- ))}
297
- </div>
298
- )}
299
-
300
- {/* Add/Edit form */}
301
- {showForm && (
302
- <div style={{
303
- background: 'var(--surface)', border: '1px solid var(--border)',
304
- borderRadius: 14, padding: '28px', marginTop: 4,
305
- }}>
306
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 20 }}>
307
- <h2 style={{ margin: 0, fontSize: 16, fontWeight: 600, color: 'var(--text)' }}>
308
- {editingId ? 'Edit Server' : 'Add MCP Server'}
309
- </h2>
310
- <button onClick={resetForm} style={{
311
- background: 'none', border: 'none', cursor: 'pointer',
312
- color: 'var(--muted)', fontSize: 18, fontFamily: 'var(--font-sans)',
313
- }}>×</button>
314
- </div>
315
-
316
- {error && (
317
- <div style={{
318
- background: 'rgba(239,68,68,0.1)', border: '1px solid rgba(239,68,68,0.25)',
319
- borderRadius: 7, padding: '9px 13px', marginBottom: 16,
320
- fontSize: 13, color: '#f87171',
321
- }}>{error}</div>
322
- )}
323
-
324
- <form onSubmit={handleSubmit}>
325
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14, marginBottom: 14 }}>
326
- <FField label="Name *" required>
327
- <input value={form.name} onChange={e => f('name', e.target.value)} placeholder="redshift-mcp"
328
- required {...inputStyle()} />
329
- <small style={{ color: 'var(--subtle)', fontSize: 11, marginTop: 3, display: 'block' }}>
330
- Tool pattern: <code style={{ fontFamily: 'var(--font-mono)' }}>mcp__name__tool</code>
331
- </small>
332
- </FField>
333
- <FField label="Transport Type">
334
- <select value={form.uiType} onChange={e => f('uiType', e.target.value as UiTransportType)} {...inputStyle()}>
335
- <option value="stdio">stdio — local subprocess</option>
336
- <option value="typescript">TypeScript — inline script</option>
337
- <option value="sse">SSE — remote Server-Sent Events</option>
338
- <option value="http">HTTP — remote HTTP transport</option>
339
- </select>
340
- </FField>
341
- </div>
342
-
343
- <FField label="Description" style={{ marginBottom: 14 }}>
344
- <input value={form.description} onChange={e => f('description', e.target.value)}
345
- placeholder="What does this MCP server provide?" {...inputStyle()} />
346
- </FField>
347
-
348
- {/* stdio fields */}
349
- {form.uiType === 'stdio' && (
350
- <>
351
- <FField label="Command *" style={{ marginBottom: 14 }}>
352
- <input value={form.command} onChange={e => f('command', e.target.value)}
353
- placeholder="node" required {...inputStyle('var(--font-mono)')} />
354
- </FField>
355
- <FField label="Arguments" hint="Comma-separated" style={{ marginBottom: 14 }}>
356
- <input value={form.args} onChange={e => f('args', e.target.value)}
357
- placeholder="/path/to/server.js, --port, 3000" {...inputStyle('var(--font-mono)')} />
358
- </FField>
359
- <EnvEntriesEditor entries={form.envEntries} envVarKeys={envVarKeys}
360
- onAdd={addEnvEntry} onRemove={removeEnvEntry} onUpdate={updateEnvEntry} />
361
- </>
362
- )}
363
-
364
- {/* TypeScript inline script */}
365
- {form.uiType === 'typescript' && (
366
- <>
367
- <FField label="TypeScript Source *" style={{ marginBottom: 14 }}
368
- hint="The runner saves this to disk and executes it with tsx. Must implement the MCP stdio protocol.">
369
- <textarea value={form.tsSource} onChange={e => f('tsSource', e.target.value)}
370
- rows={14} required {...inputStyle('var(--font-mono)')} />
371
- </FField>
372
- <EnvEntriesEditor entries={form.envEntries} envVarKeys={envVarKeys}
373
- onAdd={addEnvEntry} onRemove={removeEnvEntry} onUpdate={updateEnvEntry} />
374
- </>
375
- )}
376
-
377
- {/* SSE / HTTP fields */}
378
- {(form.uiType === 'sse' || form.uiType === 'http') && (
379
- <>
380
- <FField label="URL *" style={{ marginBottom: 14 }}>
381
- <input value={form.url} onChange={e => f('url', e.target.value)}
382
- placeholder="https://mcp.example.com/sse" required type="url" {...inputStyle('var(--font-mono)')} />
383
- </FField>
384
- <HeaderEntriesEditor
385
- entries={form.headerEntries} envVarKeys={envVarKeys}
386
- onAdd={addHeaderEntry} onRemove={removeHeaderEntry} onUpdate={updateHeaderEntry}
387
- />
388
- </>
389
- )}
390
-
391
- <label style={{ display: 'flex', alignItems: 'center', gap: 9, cursor: 'pointer', marginBottom: 20 }}>
392
- <input type="checkbox" checked={form.enabled} onChange={e => f('enabled', e.target.checked)}
393
- style={{ accentColor: 'var(--accent)', width: 14, height: 14 }} />
394
- <span style={{ fontSize: 13, color: 'var(--muted)' }}>
395
- Enabled — available for agents to use
396
- </span>
397
- </label>
398
-
399
- <div style={{ display: 'flex', gap: 10 }}>
400
- <button type="submit" disabled={saving} style={{
401
- background: saving ? 'var(--border)' : 'var(--accent)',
402
- color: '#fff', border: 'none', borderRadius: 7,
403
- padding: '9px 22px', fontSize: 13, fontWeight: 500,
404
- cursor: saving ? 'not-allowed' : 'pointer', fontFamily: 'var(--font-sans)',
405
- transition: 'opacity 0.15s',
406
- }}>{saving ? 'Saving…' : editingId ? 'Update Server' : 'Add Server'}</button>
407
- <button type="button" onClick={resetForm} style={{
408
- background: 'transparent', color: 'var(--muted)',
409
- border: '1px solid var(--border)', borderRadius: 7,
410
- padding: '9px 22px', fontSize: 13, cursor: 'pointer', fontFamily: 'var(--font-sans)',
411
- }}>Cancel</button>
412
- </div>
413
- </form>
414
- </div>
415
- )}
416
- </div>
417
- );
418
- }
419
-
420
- // ─── Env entries editor ───────────────────────────────────────────────────────
421
-
422
- function EnvEntriesEditor({
423
- entries, envVarKeys, onAdd, onRemove, onUpdate,
424
- }: {
425
- entries: EnvEntry[];
426
- envVarKeys: string[];
427
- onAdd: () => void;
428
- onRemove: (i: number) => void;
429
- onUpdate: (i: number, patch: Partial<EnvEntry>) => void;
430
- }) {
431
- return (
432
- <div style={{ marginBottom: 14 }}>
433
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
434
- <label style={{ fontSize: 12, fontWeight: 500, color: 'var(--muted)' }}>Environment Variables</label>
435
- <button type="button" onClick={onAdd} style={{
436
- background: 'none', border: '1px solid var(--border)', borderRadius: 5,
437
- color: 'var(--muted)', fontSize: 11, padding: '2px 10px', cursor: 'pointer',
438
- fontFamily: 'var(--font-sans)',
439
- }}>+ Add</button>
440
- </div>
441
-
442
- {entries.length === 0 ? (
443
- <p style={{ fontSize: 12, color: 'var(--subtle)', margin: 0, fontStyle: 'italic' }}>
444
- No env vars — click + Add to inject variables into the subprocess.
445
- </p>
446
- ) : (
447
- <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
448
- {entries.map((entry, i) => (
449
- <div key={i} style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr auto', gap: 6, alignItems: 'center' }}>
450
- {/* Key */}
451
- <input
452
- value={entry.key}
453
- onChange={e => onUpdate(i, { key: e.target.value })}
454
- placeholder="KEY_NAME"
455
- style={{
456
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
457
- padding: '6px 9px', color: 'var(--text)', fontSize: 12.5,
458
- fontFamily: 'var(--font-mono)', outline: 'none',
459
- }}
460
- />
461
- {/* Mode toggle */}
462
- <select
463
- value={entry.mode}
464
- onChange={e => onUpdate(i, { mode: e.target.value as 'value' | 'ref', val: '' })}
465
- style={{
466
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
467
- padding: '6px 8px', color: 'var(--muted)', fontSize: 11.5,
468
- fontFamily: 'var(--font-sans)', cursor: 'pointer', outline: 'none',
469
- }}
470
- >
471
- <option value="value">Custom value</option>
472
- <option value="ref">From env vars</option>
473
- </select>
474
- {/* Value or ref picker */}
475
- {entry.mode === 'value' ? (
476
- <input
477
- type="password"
478
- value={entry.val}
479
- onChange={e => onUpdate(i, { val: e.target.value })}
480
- placeholder={entry.val === '********' ? 'Current value hidden' : 'Enter value'}
481
- style={{
482
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
483
- padding: '6px 9px', color: 'var(--text)', fontSize: 12.5,
484
- fontFamily: 'var(--font-mono)', outline: 'none',
485
- }}
486
- />
487
- ) : (
488
- <select
489
- value={entry.val}
490
- onChange={e => onUpdate(i, { val: e.target.value })}
491
- style={{
492
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
493
- padding: '6px 9px', color: entry.val ? 'var(--text)' : 'var(--subtle)', fontSize: 12.5,
494
- fontFamily: 'var(--font-mono)', outline: 'none', cursor: 'pointer',
495
- }}
496
- >
497
- <option value="">— pick env var —</option>
498
- {envVarKeys.map(k => <option key={k} value={k}>{k}</option>)}
499
- {envVarKeys.length === 0 && <option disabled>No env vars — add in Settings → Env Vars</option>}
500
- </select>
501
- )}
502
- {/* Remove */}
503
- <button type="button" onClick={() => onRemove(i)} style={{
504
- background: 'none', border: 'none', color: '#ef4444', fontSize: 14,
505
- cursor: 'pointer', padding: '4px 6px', lineHeight: 1,
506
- }}>×</button>
507
- </div>
508
- ))}
509
- </div>
510
- )}
511
- </div>
512
- );
513
- }
514
-
515
- // ─── Header entries editor ────────────────────────────────────────────────────
516
-
517
- function HeaderEntriesEditor({
518
- entries, envVarKeys, onAdd, onRemove, onUpdate,
519
- }: {
520
- entries: HeaderEntry[];
521
- envVarKeys: string[];
522
- onAdd: () => void;
523
- onRemove: (i: number) => void;
524
- onUpdate: (i: number, patch: Partial<HeaderEntry>) => void;
525
- }) {
526
- return (
527
- <div style={{ marginBottom: 14 }}>
528
- <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
529
- <label style={{ fontSize: 12, fontWeight: 500, color: 'var(--muted)' }}>Headers</label>
530
- <button type="button" onClick={onAdd} style={{
531
- background: 'none', border: '1px solid var(--border)', borderRadius: 5,
532
- color: 'var(--muted)', fontSize: 11, padding: '2px 10px', cursor: 'pointer',
533
- fontFamily: 'var(--font-sans)',
534
- }}>+ Add</button>
535
- </div>
536
-
537
- {entries.length === 0 ? (
538
- <p style={{ fontSize: 12, color: 'var(--subtle)', margin: 0, fontStyle: 'italic' }}>
539
- No headers — click + Add to include HTTP headers (e.g. Authorization).
540
- </p>
541
- ) : (
542
- <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
543
- {entries.map((entry, i) => (
544
- <div key={i} style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
545
- <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr auto', gap: 6, alignItems: 'center' }}>
546
- {/* Header name */}
547
- <input
548
- value={entry.name}
549
- onChange={e => onUpdate(i, { name: e.target.value })}
550
- placeholder="Authorization"
551
- style={{
552
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
553
- padding: '6px 9px', color: 'var(--text)', fontSize: 12.5,
554
- fontFamily: 'var(--font-mono)', outline: 'none',
555
- }}
556
- />
557
- {/* Mode toggle */}
558
- <select
559
- value={entry.mode}
560
- onChange={e => onUpdate(i, { mode: e.target.value as 'value' | 'ref', val: '', prefix: '' })}
561
- style={{
562
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
563
- padding: '6px 8px', color: 'var(--muted)', fontSize: 11.5,
564
- fontFamily: 'var(--font-sans)', cursor: 'pointer', outline: 'none',
565
- }}
566
- >
567
- <option value="value">Static value</option>
568
- <option value="ref">From env var</option>
569
- </select>
570
- {/* Value or env var picker */}
571
- {entry.mode === 'value' ? (
572
- <input
573
- value={entry.val}
574
- onChange={e => onUpdate(i, { val: e.target.value })}
575
- placeholder={entry.name.toLowerCase() === 'authorization' ? 'Bearer sk-...' : 'value'}
576
- style={{
577
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
578
- padding: '6px 9px', color: 'var(--text)', fontSize: 12.5,
579
- fontFamily: 'var(--font-mono)', outline: 'none',
580
- }}
581
- />
582
- ) : (
583
- <select
584
- value={entry.val}
585
- onChange={e => onUpdate(i, { val: e.target.value })}
586
- style={{
587
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
588
- padding: '6px 9px', color: entry.val ? 'var(--text)' : 'var(--subtle)', fontSize: 12.5,
589
- fontFamily: 'var(--font-mono)', outline: 'none', cursor: 'pointer',
590
- }}
591
- >
592
- <option value="">— pick env var —</option>
593
- {envVarKeys.map(k => <option key={k} value={k}>{k}</option>)}
594
- {envVarKeys.length === 0 && <option disabled>No env vars — add in Settings → Env Vars</option>}
595
- </select>
596
- )}
597
- {/* Remove */}
598
- <button type="button" onClick={() => onRemove(i)} style={{
599
- background: 'none', border: 'none', color: '#ef4444', fontSize: 14,
600
- cursor: 'pointer', padding: '4px 6px', lineHeight: 1,
601
- }}>×</button>
602
- </div>
603
- {/* Optional prefix for env var mode */}
604
- {entry.mode === 'ref' && (
605
- <div style={{ paddingLeft: 2, display: 'flex', alignItems: 'center', gap: 6 }}>
606
- <span style={{ fontSize: 11, color: 'var(--subtle)', whiteSpace: 'nowrap' }}>Prefix (optional):</span>
607
- <input
608
- value={entry.prefix}
609
- onChange={e => onUpdate(i, { prefix: e.target.value })}
610
- placeholder='e.g. "Bearer "'
611
- style={{
612
- background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 6,
613
- padding: '4px 9px', color: 'var(--text)', fontSize: 12,
614
- fontFamily: 'var(--font-mono)', outline: 'none', width: 180,
615
- }}
616
- />
617
- <span style={{ fontSize: 11, color: 'var(--subtle)' }}>+ value of env var</span>
618
- </div>
619
- )}
620
- </div>
621
- ))}
622
- </div>
623
- )}
624
- </div>
625
- );
626
- }
627
-
628
- // ─── Server row ───────────────────────────────────────────────────────────────
629
-
630
- function ServerRow({
631
- server, isLast, onEdit, onDelete, onToggle, onTest, testResult, canEdit,
632
- }: {
633
- server: McpServer; isLast: boolean;
634
- onEdit: () => void; onDelete: () => void; onToggle: () => void; onTest: () => void;
635
- testResult?: { ok: boolean; message?: string; error?: string } | 'testing';
636
- canEdit: boolean;
637
- }) {
638
- const cfg = server.config as unknown as Record<string, unknown>;
639
- const isTs = typeof cfg.tsSource === 'string';
640
- const preview = server.type === 'stdio'
641
- ? isTs
642
- ? '[TypeScript inline script]'
643
- : `${cfg.command} ${Array.isArray(cfg.args) ? (cfg.args as string[]).join(' ') : ''}`.trim()
644
- : String(cfg.url ?? '');
645
-
646
- const envCount = Object.keys((cfg.env as object) ?? {}).length + Object.keys((cfg.envRefs as object) ?? {}).length;
647
-
648
- return (
649
- <div style={{
650
- borderBottom: isLast ? 'none' : '1px solid var(--border)',
651
- opacity: server.enabled ? 1 : 0.55,
652
- }}>
653
- <div style={{
654
- display: 'flex', alignItems: 'center', gap: 14, padding: '13px 16px',
655
- transition: 'background 0.12s',
656
- }}
657
- onMouseEnter={e => (e.currentTarget as HTMLElement).style.background = 'rgba(255,255,255,0.02)'}
658
- onMouseLeave={e => (e.currentTarget as HTMLElement).style.background = 'transparent'}
659
- >
660
- {/* Type badge */}
661
- <span style={{
662
- fontSize: 10.5, fontFamily: 'var(--font-mono)', fontWeight: 500,
663
- background: 'var(--border)', color: 'var(--muted)',
664
- padding: '2px 8px', borderRadius: 5, flexShrink: 0, letterSpacing: '0.02em',
665
- }}>{isTs ? 'ts' : server.type}</span>
666
-
667
- {/* Info */}
668
- <div style={{ flex: 1, minWidth: 0 }}>
669
- <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
670
- <span style={{ fontSize: 13.5, fontWeight: 600, color: 'var(--text)' }}>{server.name}</span>
671
- {!server.enabled && (
672
- <span style={{ fontSize: 10.5, color: 'var(--subtle)', background: 'var(--border)', padding: '1px 6px', borderRadius: 4 }}>
673
- disabled
674
- </span>
675
- )}
676
- {envCount > 0 && (
677
- <span style={{ fontSize: 10.5, color: 'var(--muted)', background: 'var(--surface-2)', padding: '1px 6px', borderRadius: 4, border: '1px solid var(--border)' }}>
678
- {envCount} env var{envCount !== 1 ? 's' : ''}
679
- </span>
680
- )}
681
- </div>
682
- {server.description && (
683
- <p style={{ margin: '1px 0 0', fontSize: 12, color: 'var(--muted)' }}>{server.description}</p>
684
- )}
685
- <p style={{
686
- margin: '2px 0 0', fontSize: 11.5, color: 'var(--subtle)',
687
- fontFamily: 'var(--font-mono)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
688
- }}>{preview}</p>
689
- </div>
690
-
691
- {/* Actions */}
692
- <div style={{ display: 'flex', gap: 6, flexShrink: 0, alignItems: 'center' }}>
693
- <ActionBtn onClick={onTest} color="var(--muted)" disabled={false}>
694
- {testResult === 'testing' ? 'Testing…' : 'Test'}
695
- </ActionBtn>
696
- <ActionBtn onClick={onToggle} color="var(--muted)" disabled={!canEdit}>
697
- {server.enabled ? 'Disable' : 'Enable'}
698
- </ActionBtn>
699
- {canEdit && <ActionBtn onClick={onEdit} color="var(--accent)">Edit</ActionBtn>}
700
- {canEdit && <ActionBtn onClick={onDelete} color="#ef4444">Delete</ActionBtn>}
701
- </div>
702
- </div>
703
-
704
- {/* Test result banner */}
705
- {testResult && testResult !== 'testing' && (
706
- <div style={{
707
- margin: '0 16px 10px', padding: '8px 12px', borderRadius: 7, fontSize: 12,
708
- background: testResult.ok ? 'rgba(5,150,105,0.08)' : 'rgba(239,68,68,0.08)',
709
- border: `1px solid ${testResult.ok ? 'rgba(5,150,105,0.25)' : 'rgba(239,68,68,0.25)'}`,
710
- color: testResult.ok ? '#059669' : '#ef4444',
711
- }}>
712
- {testResult.ok ? '✓ ' : '✗ '}{testResult.ok ? testResult.message : testResult.error}
713
- </div>
714
- )}
715
- </div>
716
- );
717
- }
718
-
719
- // ─── Primitives ───────────────────────────────────────────────────────────────
720
-
721
- function FField({ label, hint, children, style: s, required: req }: {
722
- label: string; hint?: string; children: React.ReactNode;
723
- style?: React.CSSProperties; required?: boolean;
724
- }) {
725
- return (
726
- <div style={s}>
727
- <label style={{ display: 'block', fontSize: 12, fontWeight: 500, color: 'var(--muted)', marginBottom: 5 }}>
728
- {label}{req && <span style={{ color: '#ef4444', marginLeft: 2 }}>*</span>}
729
- </label>
730
- {children}
731
- {hint && <p style={{ margin: '3px 0 0', fontSize: 11, color: 'var(--subtle)' }}>{hint}</p>}
732
- </div>
733
- );
734
- }
735
-
736
- function inputStyle(fontFamily = 'var(--font-sans)'): React.HTMLAttributes<HTMLElement> & { style: React.CSSProperties } {
737
- return {
738
- style: {
739
- width: '100%', background: 'var(--surface-2)', border: '1px solid var(--border)',
740
- borderRadius: 7, padding: '8px 11px', color: 'var(--text)',
741
- fontSize: 13, fontFamily, outline: 'none', resize: 'vertical' as const,
742
- transition: 'border-color 0.15s',
743
- } as React.CSSProperties,
744
- onFocus: (e: React.FocusEvent<HTMLElement>) => ((e.currentTarget as HTMLElement).style.borderColor = 'var(--accent)'),
745
- onBlur: (e: React.FocusEvent<HTMLElement>) => ((e.currentTarget as HTMLElement).style.borderColor = 'var(--border)'),
746
- };
747
- }
748
-
749
- function ActionBtn({ children, onClick, color, disabled }: {
750
- children: React.ReactNode; onClick: () => void; color?: string; disabled?: boolean;
751
- }) {
752
- return (
753
- <button onClick={onClick} disabled={disabled} style={{
754
- background: 'none', border: 'none', cursor: disabled ? 'not-allowed' : 'pointer',
755
- opacity: disabled ? 0.4 : 1,
756
- fontSize: 12, color: color ?? 'var(--muted)', fontFamily: 'var(--font-sans)',
757
- padding: '3px 8px', borderRadius: 5, transition: 'background 0.12s, color 0.12s',
758
- }}
759
- onMouseEnter={e => { (e.currentTarget as HTMLElement).style.background = 'rgba(255,255,255,0.06)'; }}
760
- onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = 'transparent'; }}
761
- >{children}</button>
762
- );
763
- }