@samanhappy/mcphub 1.1.13 → 1.1.15

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 (228) hide show
  1. package/README.fr.md +1 -1
  2. package/README.md +1 -1
  3. package/README.zh.md +1 -1
  4. package/dist/betterAuth.js +3 -3
  5. package/dist/betterAuth.js.map +1 -1
  6. package/dist/cli/commands/keys.js +22 -3
  7. package/dist/cli/commands/keys.js.map +1 -1
  8. package/dist/clients/mcpSdkClient.js +2667 -1448
  9. package/dist/clients/mcpSdkClient.js.map +4 -4
  10. package/dist/config/index.js +1 -1
  11. package/dist/config/index.js.map +1 -1
  12. package/dist/controllers/bearerKeyController.js +49 -3
  13. package/dist/controllers/bearerKeyController.js.map +1 -1
  14. package/dist/controllers/configController.js +1 -0
  15. package/dist/controllers/configController.js.map +1 -1
  16. package/dist/controllers/credentialBindingController.js +36 -12
  17. package/dist/controllers/credentialBindingController.js.map +1 -1
  18. package/dist/controllers/groupController.js +12 -5
  19. package/dist/controllers/groupController.js.map +1 -1
  20. package/dist/controllers/oauthRelayController.js +65 -0
  21. package/dist/controllers/oauthRelayController.js.map +1 -0
  22. package/dist/controllers/personalOAuthController.js +10 -1
  23. package/dist/controllers/personalOAuthController.js.map +1 -1
  24. package/dist/controllers/serverController.js +52 -18
  25. package/dist/controllers/serverController.js.map +1 -1
  26. package/dist/controllers/userController.js +11 -1
  27. package/dist/controllers/userController.js.map +1 -1
  28. package/dist/dao/ActivityDao.js +2 -0
  29. package/dist/dao/ActivityDao.js.map +1 -1
  30. package/dist/dao/BearerKeyDao.js +10 -5
  31. package/dist/dao/BearerKeyDao.js.map +1 -1
  32. package/dist/dao/BearerKeyDaoDbImpl.js +16 -5
  33. package/dist/dao/BearerKeyDaoDbImpl.js.map +1 -1
  34. package/dist/dao/GroupDao.js +24 -0
  35. package/dist/dao/GroupDao.js.map +1 -1
  36. package/dist/dao/GroupDaoDbImpl.js +34 -0
  37. package/dist/dao/GroupDaoDbImpl.js.map +1 -1
  38. package/dist/dao/ServerDao.js +10 -1
  39. package/dist/dao/ServerDao.js.map +1 -1
  40. package/dist/dao/ServerDaoDbImpl.js +4 -0
  41. package/dist/dao/ServerDaoDbImpl.js.map +1 -1
  42. package/dist/dao/SystemConfigDao.js +5 -2
  43. package/dist/dao/SystemConfigDao.js.map +1 -1
  44. package/dist/dao/SystemConfigDaoDbImpl.js +11 -0
  45. package/dist/dao/SystemConfigDaoDbImpl.js.map +1 -1
  46. package/dist/dao/UserDaoDbImpl.js +4 -0
  47. package/dist/dao/UserDaoDbImpl.js.map +1 -1
  48. package/dist/dao/base/JsonFileBaseDao.js +17 -2
  49. package/dist/dao/base/JsonFileBaseDao.js.map +1 -1
  50. package/dist/db/entities/Activity.js +4 -0
  51. package/dist/db/entities/Activity.js.map +1 -1
  52. package/dist/db/entities/BearerKey.js +8 -0
  53. package/dist/db/entities/BearerKey.js.map +1 -1
  54. package/dist/db/entities/Group.js +4 -0
  55. package/dist/db/entities/Group.js.map +1 -1
  56. package/dist/db/entities/Server.js +4 -0
  57. package/dist/db/entities/Server.js.map +1 -1
  58. package/dist/db/entities/SystemConfig.js +8 -0
  59. package/dist/db/entities/SystemConfig.js.map +1 -1
  60. package/dist/db/entities/User.js +4 -0
  61. package/dist/db/entities/User.js.map +1 -1
  62. package/dist/db/repositories/GroupRepository.js +36 -0
  63. package/dist/db/repositories/GroupRepository.js.map +1 -1
  64. package/dist/db/repositories/ServerRepository.js +1 -0
  65. package/dist/db/repositories/ServerRepository.js.map +1 -1
  66. package/dist/db/repositories/VectorEmbeddingRepository.js +11 -1
  67. package/dist/db/repositories/VectorEmbeddingRepository.js.map +1 -1
  68. package/dist/middlewares/auth.js +7 -1
  69. package/dist/middlewares/auth.js.map +1 -1
  70. package/dist/routes/index.js +6 -1
  71. package/dist/routes/index.js.map +1 -1
  72. package/dist/server.js +2 -0
  73. package/dist/server.js.map +1 -1
  74. package/dist/services/activityLoggingService.js +1 -0
  75. package/dist/services/activityLoggingService.js.map +1 -1
  76. package/dist/services/authorizationService.js.map +1 -1
  77. package/dist/services/capabilitySnapshotService.js +2 -1
  78. package/dist/services/capabilitySnapshotService.js.map +1 -1
  79. package/dist/services/contextCostService.js +2 -2
  80. package/dist/services/contextCostService.js.map +1 -1
  81. package/dist/services/credentialBindingService.js +9 -1
  82. package/dist/services/credentialBindingService.js.map +1 -1
  83. package/dist/services/dataService.js +4 -2
  84. package/dist/services/dataService.js.map +1 -1
  85. package/dist/services/groupService.js +21 -8
  86. package/dist/services/groupService.js.map +1 -1
  87. package/dist/services/mcpEventBus.js +6 -0
  88. package/dist/services/mcpEventBus.js.map +1 -0
  89. package/dist/services/mcpService.js +153 -6
  90. package/dist/services/mcpService.js.map +1 -1
  91. package/dist/services/personalOAuthService.js +2 -0
  92. package/dist/services/personalOAuthService.js.map +1 -1
  93. package/dist/services/principalRuntimeService.js +19 -0
  94. package/dist/services/principalRuntimeService.js.map +1 -1
  95. package/dist/services/requestContextService.js +15 -0
  96. package/dist/services/requestContextService.js.map +1 -1
  97. package/dist/services/serverConfigEnvironment.js +2 -1
  98. package/dist/services/serverConfigEnvironment.js.map +1 -1
  99. package/dist/services/smartRoutingService.js +20 -2
  100. package/dist/services/smartRoutingService.js.map +1 -1
  101. package/dist/services/sseService.js +58 -6
  102. package/dist/services/sseService.js.map +1 -1
  103. package/dist/services/templateService.js +11 -5
  104. package/dist/services/templateService.js.map +1 -1
  105. package/dist/services/toolPolicyDiagnostics.js +27 -0
  106. package/dist/services/toolPolicyDiagnostics.js.map +1 -0
  107. package/dist/services/toolPolicyService.js +41 -0
  108. package/dist/services/toolPolicyService.js.map +1 -0
  109. package/dist/services/userService.js +5 -1
  110. package/dist/services/userService.js.map +1 -1
  111. package/dist/services/vectorSearchService.js +4 -2
  112. package/dist/services/vectorSearchService.js.map +1 -1
  113. package/dist/types/toolPolicy.js +2 -0
  114. package/dist/types/toolPolicy.js.map +1 -0
  115. package/dist/utils/groupAccess.js +6 -4
  116. package/dist/utils/groupAccess.js.map +1 -1
  117. package/dist/utils/groupKeyScope.js +34 -0
  118. package/dist/utils/groupKeyScope.js.map +1 -0
  119. package/dist/utils/migration.js +10 -1
  120. package/dist/utils/migration.js.map +1 -1
  121. package/dist/utils/oauthBearer.js +1 -0
  122. package/dist/utils/oauthBearer.js.map +1 -1
  123. package/dist/utils/oauthServerMetadata.js +21 -0
  124. package/dist/utils/oauthServerMetadata.js.map +1 -1
  125. package/dist/utils/serverConfigPersistence.js +16 -1
  126. package/dist/utils/serverConfigPersistence.js.map +1 -1
  127. package/dist/utils/serverConfigValidation.js +1 -0
  128. package/dist/utils/serverConfigValidation.js.map +1 -1
  129. package/dist/utils/serverIdentity.js +7 -0
  130. package/dist/utils/serverIdentity.js.map +1 -0
  131. package/dist/utils/settingsSync.js +14 -1
  132. package/dist/utils/settingsSync.js.map +1 -1
  133. package/dist/utils/systemConfigCache.js +1 -1
  134. package/dist/utils/systemConfigCache.js.map +1 -1
  135. package/dist/utils/tokenCost.js +35 -3
  136. package/dist/utils/tokenCost.js.map +1 -1
  137. package/dist/utils/toolPolicy.js +42 -0
  138. package/dist/utils/toolPolicy.js.map +1 -0
  139. package/frontend/dist/assets/{ActivityPage-C68nRD4J.js → ActivityPage-Bi-TjKez.js} +2 -2
  140. package/frontend/dist/assets/{ActivityPage-C68nRD4J.js.map → ActivityPage-Bi-TjKez.js.map} +1 -1
  141. package/frontend/dist/assets/{ConfirmDialog-BYdfb115.js → ConfirmDialog-BJpzQmMw.js} +2 -2
  142. package/frontend/dist/assets/{ConfirmDialog-BYdfb115.js.map → ConfirmDialog-BJpzQmMw.js.map} +1 -1
  143. package/frontend/dist/assets/CredentialsPage-C4Ucer3d.js +2 -0
  144. package/frontend/dist/assets/CredentialsPage-C4Ucer3d.js.map +1 -0
  145. package/frontend/dist/assets/{Dashboard-CyTt5-52.js → Dashboard-C_2KkuoD.js} +2 -2
  146. package/frontend/dist/assets/{Dashboard-CyTt5-52.js.map → Dashboard-C_2KkuoD.js.map} +1 -1
  147. package/frontend/dist/assets/{DeleteDialog-BaCsEkJA.js → DeleteDialog-BoMYLx3J.js} +2 -2
  148. package/frontend/dist/assets/{DeleteDialog-BaCsEkJA.js.map → DeleteDialog-BoMYLx3J.js.map} +1 -1
  149. package/frontend/dist/assets/EndpointCopy-DITVC2YQ.js +2 -0
  150. package/frontend/dist/assets/{EndpointCopy-DxCJY8u6.js.map → EndpointCopy-DITVC2YQ.js.map} +1 -1
  151. package/frontend/dist/assets/GroupsPage-sE6YJyuK.js +33 -0
  152. package/frontend/dist/assets/GroupsPage-sE6YJyuK.js.map +1 -0
  153. package/frontend/dist/assets/LoginPage-CFsd_nY-.js +2 -0
  154. package/frontend/dist/assets/LoginPage-CFsd_nY-.js.map +1 -0
  155. package/frontend/dist/assets/{LogsPage-D3CAJzpq.js → LogsPage-BxZuGDuc.js} +2 -2
  156. package/frontend/dist/assets/LogsPage-BxZuGDuc.js.map +1 -0
  157. package/frontend/dist/assets/MarketPage-CDBs4vMb.js +2 -0
  158. package/frontend/dist/assets/{MarketPage-DsyVFNai.js.map → MarketPage-CDBs4vMb.js.map} +1 -1
  159. package/frontend/dist/assets/{OAuthConsentPage-go6iV2rX.js → OAuthConsentPage-BN_ucViM.js} +2 -2
  160. package/frontend/dist/assets/{OAuthConsentPage-go6iV2rX.js.map → OAuthConsentPage-BN_ucViM.js.map} +1 -1
  161. package/frontend/dist/assets/Pagination-S-7lzy59.js +2 -0
  162. package/frontend/dist/assets/Pagination-S-7lzy59.js.map +1 -0
  163. package/frontend/dist/assets/{PromptsPage-TJYCppKD.js → PromptsPage-Bl3ySXGK.js} +2 -2
  164. package/frontend/dist/assets/{PromptsPage-TJYCppKD.js.map → PromptsPage-Bl3ySXGK.js.map} +1 -1
  165. package/frontend/dist/assets/{ResourcesPage-B9ucLCSU.js → ResourcesPage-BOzQkQ92.js} +2 -2
  166. package/frontend/dist/assets/{ResourcesPage-B9ucLCSU.js.map → ResourcesPage-BOzQkQ92.js.map} +1 -1
  167. package/frontend/dist/assets/ServersPage-DHYwQ5AM.js +39 -0
  168. package/frontend/dist/assets/ServersPage-DHYwQ5AM.js.map +1 -0
  169. package/frontend/dist/assets/SettingsPage-CSJ35jlM.js +14 -0
  170. package/frontend/dist/assets/SettingsPage-CSJ35jlM.js.map +1 -0
  171. package/frontend/dist/assets/{StatusDot-hrwd7ElQ.js → StatusDot-NSjojGCy.js} +2 -2
  172. package/frontend/dist/assets/{StatusDot-hrwd7ElQ.js.map → StatusDot-NSjojGCy.js.map} +1 -1
  173. package/frontend/dist/assets/{ToggleGroup-BXCGM08S.js → ToggleGroup-BIZWpNzm.js} +2 -2
  174. package/frontend/dist/assets/{ToggleGroup-BXCGM08S.js.map → ToggleGroup-BIZWpNzm.js.map} +1 -1
  175. package/frontend/dist/assets/UserGroupSelection-CN0weGXJ.js +2 -0
  176. package/frontend/dist/assets/UserGroupSelection-CN0weGXJ.js.map +1 -0
  177. package/frontend/dist/assets/UsersPage-DC51v2b8.js +2 -0
  178. package/frontend/dist/assets/UsersPage-DC51v2b8.js.map +1 -0
  179. package/frontend/dist/assets/clipboard-ik5YV95M.js +2 -0
  180. package/frontend/dist/assets/{clipboard-CoLstiwP.js.map → clipboard-ik5YV95M.js.map} +1 -1
  181. package/frontend/dist/assets/{CopyClientConfigDialog-Clr8hk1S.js → costRevision-Bxov9_vH.js} +4 -4
  182. package/frontend/dist/assets/costRevision-Bxov9_vH.js.map +1 -0
  183. package/frontend/dist/assets/framework-vendor-D0S_ys6r.js +61 -0
  184. package/frontend/dist/assets/framework-vendor-D0S_ys6r.js.map +1 -0
  185. package/frontend/dist/assets/{i18n-vendor-DVmjrzgW.js → i18n-vendor-COTriSc6.js} +2 -2
  186. package/frontend/dist/assets/{i18n-vendor-DVmjrzgW.js.map → i18n-vendor-COTriSc6.js.map} +1 -1
  187. package/frontend/dist/assets/{icons-vendor-CkcU1LMA.js → icons-vendor-DZjjGrOu.js} +3 -3
  188. package/frontend/dist/assets/{icons-vendor-CkcU1LMA.js.map → icons-vendor-DZjjGrOu.js.map} +1 -1
  189. package/frontend/dist/assets/index-6AyPtx0A.js +3 -0
  190. package/frontend/dist/assets/index-6AyPtx0A.js.map +1 -0
  191. package/frontend/dist/assets/index-DrHctFvx.css +1 -0
  192. package/frontend/dist/assets/{resourceService-DXuM3elu.js → resourceService-DMzCAZ0B.js} +2 -2
  193. package/frontend/dist/assets/{resourceService-DXuM3elu.js.map → resourceService-DMzCAZ0B.js.map} +1 -1
  194. package/frontend/dist/assets/useCostData-CcFSYViI.js +2 -0
  195. package/frontend/dist/assets/useCostData-CcFSYViI.js.map +1 -0
  196. package/frontend/dist/assets/variableDetection-CbYSa1ka.js +80 -0
  197. package/frontend/dist/assets/variableDetection-CbYSa1ka.js.map +1 -0
  198. package/frontend/dist/index.html +5 -5
  199. package/mcp_settings.json +2 -0
  200. package/package.json +2 -2
  201. package/frontend/dist/assets/CopyClientConfigDialog-Clr8hk1S.js.map +0 -1
  202. package/frontend/dist/assets/CredentialsPage-WxYmPkS2.js +0 -2
  203. package/frontend/dist/assets/CredentialsPage-WxYmPkS2.js.map +0 -1
  204. package/frontend/dist/assets/EndpointCopy-DxCJY8u6.js +0 -2
  205. package/frontend/dist/assets/GroupsPage-C9uf7qRF.js +0 -33
  206. package/frontend/dist/assets/GroupsPage-C9uf7qRF.js.map +0 -1
  207. package/frontend/dist/assets/LoginPage-CfljHYdH.js +0 -2
  208. package/frontend/dist/assets/LoginPage-CfljHYdH.js.map +0 -1
  209. package/frontend/dist/assets/LogsPage-D3CAJzpq.js.map +0 -1
  210. package/frontend/dist/assets/MarketPage-DsyVFNai.js +0 -2
  211. package/frontend/dist/assets/Pagination-BRqqU5Ct.js +0 -2
  212. package/frontend/dist/assets/Pagination-BRqqU5Ct.js.map +0 -1
  213. package/frontend/dist/assets/ServersPage-BO1e0GJ4.js +0 -39
  214. package/frontend/dist/assets/ServersPage-BO1e0GJ4.js.map +0 -1
  215. package/frontend/dist/assets/SettingsPage-_LJt6T_U.js +0 -14
  216. package/frontend/dist/assets/SettingsPage-_LJt6T_U.js.map +0 -1
  217. package/frontend/dist/assets/UsersPage-CLPsZo0-.js +0 -2
  218. package/frontend/dist/assets/UsersPage-CLPsZo0-.js.map +0 -1
  219. package/frontend/dist/assets/clipboard-CoLstiwP.js +0 -2
  220. package/frontend/dist/assets/framework-vendor-CQFm9vKd.js +0 -61
  221. package/frontend/dist/assets/framework-vendor-CQFm9vKd.js.map +0 -1
  222. package/frontend/dist/assets/index-BE6bm3KN.css +0 -1
  223. package/frontend/dist/assets/index-CKByfAIo.js +0 -3
  224. package/frontend/dist/assets/index-CKByfAIo.js.map +0 -1
  225. package/frontend/dist/assets/useCostData-Br7dI3R9.js +0 -2
  226. package/frontend/dist/assets/useCostData-Br7dI3R9.js.map +0 -1
  227. package/frontend/dist/assets/variableDetection-Civ1fQ-c.js +0 -18
  228. package/frontend/dist/assets/variableDetection-Civ1fQ-c.js.map +0 -1
@@ -6,11 +6,11 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>MCPHub Dashboard</title>
8
8
  <link rel="icon" type="image/x-icon" href="./assets/favicon-DJnZjsC8.ico">
9
- <script type="module" crossorigin src="./assets/index-CKByfAIo.js"></script>
10
- <link rel="modulepreload" crossorigin href="./assets/framework-vendor-CQFm9vKd.js">
11
- <link rel="modulepreload" crossorigin href="./assets/i18n-vendor-DVmjrzgW.js">
12
- <link rel="modulepreload" crossorigin href="./assets/icons-vendor-CkcU1LMA.js">
13
- <link rel="stylesheet" crossorigin href="./assets/index-BE6bm3KN.css">
9
+ <script type="module" crossorigin src="./assets/index-6AyPtx0A.js"></script>
10
+ <link rel="modulepreload" crossorigin href="./assets/framework-vendor-D0S_ys6r.js">
11
+ <link rel="modulepreload" crossorigin href="./assets/i18n-vendor-COTriSc6.js">
12
+ <link rel="modulepreload" crossorigin href="./assets/icons-vendor-DZjjGrOu.js">
13
+ <link rel="stylesheet" crossorigin href="./assets/index-DrHctFvx.css">
14
14
  </head>
15
15
 
16
16
  <body class="bg-gray-100">
package/mcp_settings.json CHANGED
@@ -37,6 +37,8 @@
37
37
  },
38
38
  "users": [],
39
39
  "systemConfig": {
40
+ "dashboardName": "",
41
+ "toolPolicies": [],
40
42
  "oauthServer": {
41
43
  "enabled": true,
42
44
  "accessTokenLifetime": 3600,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samanhappy/mcphub",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
4
4
  "description": "Open-source, self-hosted MCP gateway and control plane for connecting, controlling, and operating MCP servers.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -135,7 +135,7 @@
135
135
  "zod": "^4.4.3"
136
136
  },
137
137
  "engines": {
138
- "node": ">=20.0.0"
138
+ "node": ">=22.0.0"
139
139
  },
140
140
  "scripts": {
141
141
  "build": "pnpm backend:build && pnpm frontend:build",
@@ -1 +0,0 @@
1
- {"version":3,"file":"CopyClientConfigDialog-Clr8hk1S.js","sources":["../../src/utils/toolDescription.ts","../../src/utils/mcpClientSnippets.ts","../../src/components/ui/CopyClientConfigDialog.tsx"],"sourcesContent":["type ToolDescriptionSource = {\n description?: string;\n defaultDescription?: string;\n hasDescriptionOverride?: boolean;\n};\n\nexport interface ToolDescriptionInfo {\n currentDescription: string;\n defaultDescription?: string;\n hasDescriptionOverride: boolean;\n}\n\nconst normalizeDescription = (description: string | undefined, fallback: string): string => {\n return description && description.trim() ? description : fallback;\n};\n\nexport const getToolDescriptionInfo = (\n tool: ToolDescriptionSource,\n noDescriptionText: string,\n): ToolDescriptionInfo => {\n const currentDescription = normalizeDescription(tool.description, noDescriptionText);\n\n if (!tool.hasDescriptionOverride) {\n return {\n currentDescription,\n hasDescriptionOverride: false,\n };\n }\n\n return {\n currentDescription,\n defaultDescription: normalizeDescription(tool.defaultDescription, noDescriptionText),\n hasDescriptionOverride: true,\n };\n};\n","/**\n * Per-client MCP configuration presets for the copy actions (#1165).\n *\n * MCPHub hands out HTTP endpoints, so every preset describes the same target -\n * a name, an optional URL/headers pair (HTTP) or command/args/env triple\n * (stdio) - in the shape the selected client expects. Eleven of the fourteen\n * presets were verified against the upstream sources below (URLs re-checked\n * 2026-09, the month this file landed); the remaining three reuse the generic\n * `mcpServers` shape (or, for the generic-http escape hatch, the bare entry)\n * these copy actions always produced.\n *\n * Which clients document an explicit transport field, and which do not:\n * - Documents a transport field (the `type` key is written into the entry):\n * claude-code (`type: \"http\" | \"sse\" | \"stdio\"`; a remote entry with `url`\n * but no `type` is a configuration error per\n * https://code.claude.com/docs/en/mcp), vscode (`\"http\" | \"sse\" | \"stdio\"`,\n * final URL after the 2026-09 migration:\n * https://code.visualstudio.com/docs/agent-customization/mcp-servers),\n * codebuddy (`type` marked Required; fixed `\"http\"` for HTTP, `\"sse\"` for\n * SSE and `\"stdio\"` for stdio, per\n * https://www.codebuddy.ai/docs/cli/mcp), qoder (every remote\n * example carries `type`; for Streamable HTTP the doc says to configure the\n * URL the same way as SSE and the IDE auto-detects it, so the http target\n * deliberately keeps the bare `url` + `headers` shape rather than tagging\n * it `sse`; https://docs.qoder.com/zh/user-guide/chat/model-context-protocol),\n * cherry-studio (`baseUrl` + `'sse' | 'streamableHttp'`, strict schema),\n * opencode (`type: 'remote' | 'local'`).\n * - Field table does not list a transport key: cursor\n * (https://cursor.com/docs/mcp), trae (https://docs.trae.ai/ide/add-mcp-servers),\n * zcode (https://zcode.z.ai/en/docs/mcp-services), windsurf\n * (https://docs.windsurf.com/windsurf/cascade/mcp). The `type: \"sse\"` marker\n * the generic shape injects for SSE targets on these presets is added on\n * the assumption that unknown keys are ignored, not required by their\n * documented shape; it is kept because SSE upstreams must not be dialed as\n * streamable HTTP.\n * - Verified but the format carries no `type` key at all: codex (TOML;\n * `codex-rs/config/src/mcp_types.rs`,\n * https://github.com/openai/codex/blob/main/codex-rs/config/src/mcp_types.rs).\n * (The generic-http escape hatch, one of the three unverified presets, is not\n * a \"no `type`\" format: for a remote target it always writes an explicit\n * `type` - `\"http\"`, or `\"sse\"` for an SSE upstream.)\n *\n * Upstream sources used:\n * - Claude Code MCP configuration: https://code.claude.com/docs/en/mcp\n * - Cursor MCP docs: https://cursor.com/docs/mcp\n * - VS Code MCP configuration reference:\n * https://code.visualstudio.com/docs/agent-customization/mcp-servers\n * - Codex `codex-rs/config/src/mcp_types.rs`:\n * https://github.com/openai/codex/blob/main/codex-rs/config/src/mcp_types.rs\n * - OpenCode mcp-servers docs: https://opencode.ai/docs/mcp-servers/\n * - Cherry Studio `mcpProtocolInstall.ts`:\n * https://github.com/CherryHQ/cherry-studio/blob/main/src/shared/data/types/mcpProtocolInstall.ts\n * - CodeBuddy MCP CLI docs: https://www.codebuddy.ai/docs/cli/mcp\n * - Qoder MCP user guide:\n * https://docs.qoder.com/zh/user-guide/chat/model-context-protocol\n * - Trae MCP docs: https://docs.trae.ai/ide/add-mcp-servers\n * - Windsurf MCP docs: https://docs.windsurf.com/windsurf/cascade/mcp\n * - ZCode MCP docs: https://zcode.z.ai/en/docs/mcp-services\n */\n\nexport type ClientSnippetId =\n | 'generic-mcp-servers'\n | 'generic-http'\n | 'claude-code'\n | 'cursor'\n | 'vscode'\n | 'codex'\n | 'opencode'\n | 'windsurf'\n | 'cherry-studio'\n | 'codebuddy'\n | 'qoder'\n | 'trae'\n | 'zcode'\n | 'workbuddy';\n\nexport interface ClientSnippetPreset {\n id: ClientSnippetId;\n}\n\n/** Tab the dialog opens on; the shape the copy actions produced before. */\nexport const DEFAULT_CLIENT_SNIPPET_ID: ClientSnippetId = 'generic-mcp-servers';\n\n/**\n * Tabs shown in the copy dialog, most common clients first. The first entry is\n * the shape the copy actions produced before the presets existed, so the\n * default tab stays familiar; the bare HTTP entry is the escape hatch for\n * clients that wrap the entry in their own container.\n */\nexport const CLIENT_SNIPPET_PRESETS: readonly ClientSnippetPreset[] = [\n { id: 'generic-mcp-servers' },\n { id: 'claude-code' },\n { id: 'cursor' },\n { id: 'vscode' },\n { id: 'codex' },\n { id: 'opencode' },\n { id: 'windsurf' },\n { id: 'cherry-studio' },\n { id: 'codebuddy' },\n { id: 'qoder' },\n { id: 'trae' },\n { id: 'zcode' },\n { id: 'workbuddy' },\n { id: 'generic-http' },\n];\n\nexport interface ClientSnippetTarget {\n name: string;\n /** Stored transport type; `sse` is called out so clients do not misdial it as http. */\n type?: 'stdio' | 'sse' | 'streamable-http' | 'openapi';\n /** HTTP targets carry a URL (and usually an Authorization header). */\n url?: string;\n headers?: Record<string, string>;\n /** stdio targets carry a command instead. */\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n description?: string;\n}\n\nexport interface ClientSnippetBlock {\n kind: 'config' | 'command';\n text: string;\n}\n\nexport interface ClientSnippetOptions {\n /** Description used by the VS Code token input prompt. */\n tokenPromptDescription?: string;\n}\n\n/** Placeholder MCPHub uses for snippets that need a token pasted in by hand. */\nexport const ACCESS_TOKEN_PLACEHOLDER = '<your-access-token>';\nconst TOKEN_INPUT_ID = 'mcphub-token';\n\nconst isHttpTarget = (target: ClientSnippetTarget): boolean => Boolean(target.url);\n\nconst definedHeaders = (target: ClientSnippetTarget): Record<string, string> | undefined => {\n const entries = Object.entries(target.headers ?? {}).filter(([, value]) => value !== undefined);\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst definedEnv = (target: ClientSnippetTarget): Record<string, string> | undefined => {\n const entries = Object.entries(target.env ?? {}).filter(([, value]) => value !== undefined);\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst stdioEntry = (target: ClientSnippetTarget): Record<string, unknown> => {\n const args = target.args?.filter((arg) => arg !== undefined) ?? [];\n const env = definedEnv(target);\n return {\n command: target.command ?? '',\n ...(args.length > 0 ? { args } : {}),\n ...(env ? { env } : {}),\n };\n};\n\n/**\n * HTTP/stdio entry for clients whose docs make the transport a required field.\n * Claude Code skips an entry that has a `url` but no `type` (it reads the\n * entry as stdio and reports a configuration error), and CodeBuddy's field\n * table marks `type` as Required, fixed to `\"http\"` for the HTTP form and\n * `\"sse\"` for the SSE form.\n *\n * Stdio targets differ between the two clients. Claude Code reads a type-less\n * entry as stdio (https://code.claude.com/docs/en/mcp), so its stdio shape\n * keeps the plain command/args/env form with no `type` key. CodeBuddy's docs\n * mark `type` as Required with the fixed value `\"stdio\"`\n * (https://www.codebuddy.ai/docs/cli/mcp), so its stdio shape writes\n * `\"type\": \"stdio\"`; pass `stdioType` only for CodeBuddy.\n */\nconst typedTransportEntry = (\n target: ClientSnippetTarget,\n stdioType: boolean = false,\n): Record<string, unknown> => {\n if (!isHttpTarget(target)) {\n return stdioType ? { type: 'stdio', ...stdioEntry(target) } : stdioEntry(target);\n }\n\n const headers = definedHeaders(target);\n return {\n type: target.type === 'sse' ? 'sse' : 'http',\n url: target.url ?? '',\n ...(headers ? { headers } : {}),\n };\n};\n\n// This helper feeds the generic `mcpServers` shape for the generic-mcp-servers,\n// qoder, cursor, windsurf, trae, zcode and workbuddy presets. Only qoder's\n// upstream docs carry a transport field for this shape; for the rest\n// (generic-mcp-servers, cursor, windsurf, trae, zcode, workbuddy) the\n// `type: \"sse\"` marker is added on the assumption that unknown keys are\n// ignored, so it is unverified against a field table. It is kept because an\n// SSE upstream must not be dialed as streamable HTTP.\nconst httpEntry = (target: ClientSnippetTarget): Record<string, unknown> => {\n const headers = definedHeaders(target);\n return {\n // Only `sse` is spelled out: it is the one HTTP transport a client might\n // otherwise misdial as streamable HTTP. Every other target keeps the bare\n // `url` + `headers` shape these presets always produced.\n ...(target.type === 'sse' ? { type: 'sse' } : {}),\n url: target.url ?? '',\n ...(headers ? { headers } : {}),\n };\n};\n\nconst withWrappedName = (\n wrapper: string,\n name: string,\n entry: Record<string, unknown>,\n): string => JSON.stringify({ [wrapper]: { [name]: entry } }, null, 2);\n\nconst jsonSnippet = (target: ClientSnippetTarget): string =>\n withWrappedName(\n 'mcpServers',\n target.name,\n isHttpTarget(target) ? httpEntry(target) : stdioEntry(target),\n );\n\n// VS Code wants the transport spelled out and can prompt for the token itself,\n// so the shared placeholder becomes an input reference plus an `inputs` entry.\nconst vscodeSnippet = (target: ClientSnippetTarget, options: ClientSnippetOptions): string => {\n const headers = definedHeaders(target);\n const placeholder = `Bearer ${ACCESS_TOKEN_PLACEHOLDER}`;\n const needsTokenInput = headers?.Authorization === placeholder;\n\n const body = needsTokenInput\n ? { ...headers, Authorization: `Bearer \\${input:${TOKEN_INPUT_ID}}` }\n : headers;\n\n const entry = isHttpTarget(target)\n ? {\n // VS Code distinguishes the SSE transport from streamable HTTP.\n type: target.type === 'sse' ? 'sse' : 'http',\n url: target.url,\n ...(body ? { headers: body } : {}),\n }\n : { type: 'stdio', ...stdioEntry(target) };\n\n return JSON.stringify(\n {\n servers: { [target.name]: entry },\n ...(needsTokenInput\n ? {\n inputs: [\n {\n id: TOKEN_INPUT_ID,\n type: 'promptString',\n description: options.tokenPromptDescription ?? 'MCPHub access token',\n password: true,\n },\n ],\n }\n : {}),\n },\n null,\n 2,\n );\n};\n\nconst TOML_BARE_KEY = /^[A-Za-z0-9_-]+$/;\n\nconst tomlKey = (key: string): string => (TOML_BARE_KEY.test(key) ? key : JSON.stringify(key));\nconst tomlValue = (value: string): string => JSON.stringify(value);\nconst tomlInlineTable = (entries: Record<string, string>): string =>\n `{ ${Object.entries(entries)\n .map(([key, value]) => `${tomlKey(key)} = ${tomlValue(value)}`)\n .join(', ')} }`;\n\n// Codex calls the header map `http_headers` (there is no `headers` key), and\n// needs quoted table keys for names that are not bare TOML keys - group names\n// may contain spaces or CJK. Note `#[schemars(deny_unknown_fields)]` in\n// `mcp_types.rs` only constrains the generated JSON Schema; the Rust struct\n// itself has no `#[serde(deny_unknown_fields)]`, so TOML parsing may silently\n// ignore unknown fields instead of rejecting them.\nconst codexSnippet = (target: ClientSnippetTarget): string => {\n const key = tomlKey(target.name);\n const lines = [`[mcp_servers.${key}]`];\n\n if (isHttpTarget(target)) {\n lines.push(`url = ${tomlValue(target.url ?? '')}`);\n const headers = definedHeaders(target);\n if (headers) {\n lines.push(`http_headers = ${tomlInlineTable(headers)}`);\n }\n return lines.join('\\n');\n }\n\n lines.push(`command = ${tomlValue(target.command ?? '')}`);\n const args = target.args?.filter((arg) => arg !== undefined) ?? [];\n if (args.length > 0) {\n lines.push(`args = [${args.map(tomlValue).join(', ')}]`);\n }\n const env = definedEnv(target);\n if (env) {\n lines.push('', `[mcp_servers.${key}.env]`);\n for (const [name, value] of Object.entries(env)) {\n lines.push(`${tomlKey(name)} = ${tomlValue(value)}`);\n }\n }\n return lines.join('\\n');\n};\n\nconst openCodeSnippet = (target: ClientSnippetTarget): string => {\n if (!isHttpTarget(target)) {\n const env = definedEnv(target);\n const args = target.args?.filter((arg) => arg !== undefined) ?? [];\n return JSON.stringify(\n {\n mcp: {\n [target.name]: {\n type: 'local',\n command: [target.command ?? '', ...args],\n ...(env ? { environment: env } : {}),\n },\n },\n },\n null,\n 2,\n );\n }\n\n const headers = definedHeaders(target);\n return JSON.stringify(\n {\n mcp: {\n [target.name]: {\n type: 'remote',\n url: target.url,\n ...(headers ? { headers } : {}),\n },\n },\n },\n null,\n 2,\n );\n};\n\n// Cherry Studio's `ProtocolMcpServerConfigSchema` is a strict object: remote\n// servers use `baseUrl` + `type: 'sse' | 'streamableHttp'` (no `url` key) and\n// stdio servers use command/args/env. The install-time metadata\n// (`installSource`, `isTrusted`, `installedAt`, `isActive: false`) belongs to\n// `ProtocolMcpServerInstallSchema`, the install *request* schema, not to this\n// hand-pasted config; an entry carrying `isActive` would not satisfy\n// `ProtocolMcpServerConfigSchema` (which has no such key).\nconst cherryStudioSnippet = (target: ClientSnippetTarget): string => {\n const base = {\n name: target.name,\n description: target.description ?? '',\n };\n\n const headers = definedHeaders(target);\n const entry = isHttpTarget(target)\n ? {\n ...base,\n // The schema accepts exactly two remote transports; keep SSE upstreams\n // spelled out so Cherry Studio does not dial them as streamable HTTP.\n type: target.type === 'sse' ? 'sse' : 'streamableHttp',\n baseUrl: target.url,\n ...(headers ? { headers } : {}),\n }\n : { ...base, type: 'stdio', ...stdioEntry(target) };\n\n return withWrappedName('mcpServers', target.name, entry);\n};\n\nconst CLI_SAFE = /^[A-Za-z0-9._:@/=-]+$/;\n\n// Single quotes are the only reliable POSIX quoting for a pasted command:\n// double quotes still let the shell expand `$(...)`, backticks and `${...}`\n// inside them. Within single quotes nothing is expanded, so the only character\n// needing special handling is `'` itself, escaped as `'\\''` (close quote,\n// escaped quote, reopen).\nconst shellQuote = (value: string): string => `'${value.replace(/'/g, \"'\\\\''\")}'`;\n\nconst cliArg = (value: string): string =>\n CLI_SAFE.test(value) ? value : shellQuote(value);\n\n// Claude Code's one-liner equivalent of the JSON block, in the documented\n// HTTP shape: `claude mcp add --transport <t> <name> <url> [--header ...]`.\n//\n// A positional that starts with `-` cannot be spelled safely in that shape:\n// shell quoting is gone by the time the CLI's tokenizer sees the value, and\n// the documented `--` separator belongs to the stdio form, so relying on it\n// for the HTTP form would be an unverified parsing assumption. Since\n// SERVER_NAME_PATTERN admits names like `-foo`, a name or URL starting with\n// a dash omits the command block outright - the JSON config block still\n// carries the full entry - rather than emitting a command the CLI would\n// misparse as one of its own options.\nconst claudeCodeCommand = (target: ClientSnippetTarget): string | undefined => {\n if (!isHttpTarget(target)) {\n return undefined;\n }\n\n if (target.name.startsWith('-') || (target.url ?? '').startsWith('-')) {\n return undefined;\n }\n\n const parts = [\n 'claude',\n 'mcp',\n 'add',\n '--transport',\n // SSE endpoints must not be dialed as streamable HTTP.\n target.type === 'sse' ? 'sse' : 'http',\n cliArg(target.name),\n cliArg(target.url ?? ''),\n ];\n for (const [name, value] of Object.entries(definedHeaders(target) ?? {})) {\n parts.push('--header', cliArg(`${name}: ${value}`));\n }\n return parts.join(' ');\n};\n\nconst asString = (value: unknown): string | undefined =>\n typeof value === 'string' && value.length > 0 ? value : undefined;\n\n/** Narrow a stored `type` to the four legal server types; anything else is dropped. */\nconst asServerType = (value: unknown): ClientSnippetTarget['type'] => {\n switch (value) {\n case 'stdio':\n case 'sse':\n case 'streamable-http':\n case 'openapi':\n return value;\n default:\n return undefined;\n }\n};\n\nconst asStringRecord = (value: unknown): Record<string, string> | undefined => {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined;\n }\n\n const entries = Object.entries(value as Record<string, unknown>).filter(\n (entry): entry is [string, string] => typeof entry[1] === 'string',\n );\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n};\n\nconst asStringArray = (value: unknown): string[] | undefined => {\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const items = value.filter((item): item is string => typeof item === 'string');\n return items.length > 0 ? items : undefined;\n};\n\n/**\n * Narrow a stored server configuration (the `mcp-settings/export` payload\n * carries MCPHub's internal fields too) down to what a client snippet needs.\n */\nexport const toClientSnippetTarget = (\n name: string,\n config: Record<string, unknown> | undefined,\n): ClientSnippetTarget => {\n const source = config ?? {};\n\n return {\n name,\n ...(asServerType(source.type) ? { type: asServerType(source.type) } : {}),\n ...(asString(source.url) ? { url: asString(source.url) } : {}),\n ...(asStringRecord(source.headers) ? { headers: asStringRecord(source.headers) } : {}),\n ...(asString(source.command) ? { command: asString(source.command) } : {}),\n ...(asStringArray(source.args) ? { args: asStringArray(source.args) } : {}),\n ...(asStringRecord(source.env) ? { env: asStringRecord(source.env) } : {}),\n ...(asString(source.description) ? { description: asString(source.description) } : {}),\n };\n};\n\nconst configSnippet = (\n id: ClientSnippetId,\n target: ClientSnippetTarget,\n options: ClientSnippetOptions,\n): string => {\n switch (id) {\n case 'generic-mcp-servers':\n return jsonSnippet(target);\n case 'generic-http':\n return JSON.stringify(\n isHttpTarget(target)\n ? { type: 'http', ...httpEntry(target) }\n : stdioEntry(target),\n null,\n 2,\n );\n case 'vscode':\n return vscodeSnippet(target, options);\n case 'codex':\n return codexSnippet(target);\n case 'opencode':\n return openCodeSnippet(target);\n case 'cherry-studio':\n return cherryStudioSnippet(target);\n case 'claude-code':\n return withWrappedName('mcpServers', target.name, typedTransportEntry(target));\n case 'codebuddy':\n // CodeBuddy's field table marks `type` as Required, fixed to `\"stdio\"`\n // for stdio entries, so unlike Claude Code it always carries the type.\n return withWrappedName('mcpServers', target.name, typedTransportEntry(target, true));\n default:\n return jsonSnippet(target);\n }\n};\n\nexport const buildClientSnippetBlocks = (\n id: ClientSnippetId,\n target: ClientSnippetTarget,\n options: ClientSnippetOptions = {},\n): ClientSnippetBlock[] => {\n // OpenAPI-backed servers carry neither a URL nor a command, so every preset\n // would degrade to filler like `command: \"\"`. Return no blocks and let the\n // dialog explain that there is no client-side transport to copy.\n if (!target.url && !target.command) {\n return [];\n }\n\n const blocks: ClientSnippetBlock[] = [{ kind: 'config', text: configSnippet(id, target, options) }];\n\n if (id === 'claude-code') {\n const command = claudeCodeCommand(target);\n if (command) {\n blocks.push({ kind: 'command', text: command });\n }\n }\n\n return blocks;\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Check, Copy, X } from 'lucide-react';\nimport {\n buildClientSnippetBlocks,\n CLIENT_SNIPPET_PRESETS,\n DEFAULT_CLIENT_SNIPPET_ID,\n type ClientSnippetId,\n type ClientSnippetTarget,\n} from '@/utils/mcpClientSnippets';\nimport { copyText } from '@/utils/clipboard';\nimport { useToast } from '@/contexts/ToastContext';\n\ninterface CopyClientConfigDialogProps {\n isOpen: boolean;\n onClose: () => void;\n /** Target to render; `null` keeps the dialog closed. */\n target: ClientSnippetTarget | null;\n}\n\n/**\n * Per-client MCP configuration presets for the copy actions (#1165): one tab\n * per client, each with the path it belongs in and a ready-to-paste snippet.\n */\nconst CopyClientConfigDialog = ({ isOpen, onClose, target }: CopyClientConfigDialogProps) => {\n const { t } = useTranslation();\n const { showToast } = useToast();\n const [activeId, setActiveId] = useState<ClientSnippetId>(DEFAULT_CLIENT_SNIPPET_ID);\n const [copiedBlock, setCopiedBlock] = useState<string | null>(null);\n const dialogRef = useRef<HTMLDivElement>(null);\n const returnFocusRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!isOpen) {\n setCopiedBlock(null);\n setActiveId(DEFAULT_CLIENT_SNIPPET_ID);\n }\n }, [isOpen]);\n\n // Like the other hand-rolled modals in this codebase the dialog does not trap\n // focus; it does move focus inside on open and hand it back to the trigger on\n // close, so keyboard users are not left behind on the page underneath.\n useEffect(() => {\n if (!isOpen) return;\n returnFocusRef.current =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n dialogRef.current?.focus();\n\n return () => {\n returnFocusRef.current?.focus();\n };\n }, [isOpen]);\n\n useEffect(() => {\n if (!isOpen) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose();\n };\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [isOpen, onClose]);\n\n const blocks = useMemo(\n () =>\n target\n ? buildClientSnippetBlocks(activeId, target, {\n tokenPromptDescription: t('clientConfig.tokenPrompt'),\n })\n : [],\n [activeId, target, t],\n );\n\n if (!isOpen || !target) {\n return null;\n }\n\n const handleCopy = async (key: string, value: string) => {\n const ok = await copyText(value);\n if (!ok) {\n showToast(t('common.copyFailed'), 'error');\n return;\n }\n setCopiedBlock(key);\n showToast(t('common.copySuccess'), 'success');\n setTimeout(() => setCopiedBlock((current) => (current === key ? null : current)), 1500);\n };\n\n return (\n <div\n className=\"fixed inset-0 bg-black/50 z-[100] flex items-center justify-center p-4\"\n onClick={(event) => {\n if (event.target === event.currentTarget) onClose();\n }}\n >\n <div\n ref={dialogRef}\n tabIndex={-1}\n className=\"hub-card w-full max-w-2xl\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={t('clientConfig.title')}\n >\n <div\n className=\"flex items-start justify-between gap-3 px-4 py-3\"\n style={{ borderBottom: '1px solid var(--hub-line-2)' }}\n >\n <div className=\"min-w-0\">\n <div className=\"text-[14px] font-medium\" style={{ color: 'var(--hub-ink)' }}>\n {t('clientConfig.title')}\n </div>\n <div className=\"text-[12px] mt-0.5\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('clientConfig.description', { name: target.name })}\n </div>\n </div>\n <button className=\"hub-icon-btn sm\" onClick={onClose} aria-label={t('app.closeButton')}>\n <X size={13} />\n </button>\n </div>\n\n {/* A target with neither URL nor command (OpenAPI-backed servers) has no\n transport, so every preset returns the same empty result: offering 14\n clickable tabs would only suggest the dialog is broken. The tab strip\n and its per-client hint are therefore hidden and only the explanation\n below is shown. */}\n {blocks.length > 0 && (\n <div className=\"px-4 pt-3\">\n <div\n className=\"flex flex-wrap gap-1\"\n role=\"tablist\"\n aria-label={t('clientConfig.title')}\n >\n {CLIENT_SNIPPET_PRESETS.map((preset) => {\n const active = preset.id === activeId;\n return (\n <button\n key={preset.id}\n role=\"tab\"\n aria-selected={active}\n onClick={() => setActiveId(preset.id)}\n className=\"px-2.5 py-1 rounded-md text-[12px] transition-colors hover:bg-[var(--hub-surface-hover)]\"\n style={{\n background: active ? 'var(--hub-surface)' : 'transparent',\n border: '1px solid ' + (active ? 'var(--hub-line)' : 'transparent'),\n color: active ? 'var(--hub-ink)' : 'var(--hub-ink-2)',\n }}\n >\n {t(`clientConfig.clients.${preset.id}.label`)}\n </button>\n );\n })}\n </div>\n\n <p className=\"text-[12px] mt-3\" style={{ color: 'var(--hub-ink-3)' }}>\n {t(`clientConfig.clients.${activeId}.hint`)}\n </p>\n </div>\n )}\n\n <div className=\"px-4 pb-4 pt-2 space-y-3 max-h-[60vh] overflow-auto\">\n {blocks.length === 0 ? (\n // OpenAPI-backed servers have no client-side transport to copy.\n <p className=\"text-[12px]\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('clientConfig.noTransport')}\n </p>\n ) : (\n blocks.map((block) => {\n const key = `${activeId}:${block.kind}`;\n const copied = copiedBlock === key;\n return (\n <div key={key}>\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"text-[12px]\" style={{ color: 'var(--hub-ink-2)' }}>\n {t(\n block.kind === 'command'\n ? 'clientConfig.commandBlock'\n : 'clientConfig.configBlock',\n )}\n </span>\n <button\n className=\"hub-btn sm\"\n onClick={() => void handleCopy(key, block.text)}\n >\n {copied ? <Check size={12} className=\"text-[var(--hub-ok)]\" /> : <Copy size={12} />}\n {copied ? t('clientConfig.copied') : t('common.copy')}\n </button>\n </div>\n <pre\n className=\"hub-mono m-0 p-3 rounded-md overflow-auto\"\n style={{\n background: 'var(--hub-bg-2)',\n border: '1px solid var(--hub-line-2)',\n fontSize: 12,\n lineHeight: 1.5,\n color: 'var(--hub-ink-2)',\n maxHeight: 320,\n }}\n >\n <code>{block.text}</code>\n </pre>\n </div>\n );\n })\n )}\n </div>\n\n <div\n className=\"flex justify-end px-4 py-3\"\n style={{ borderTop: '1px solid var(--hub-line-2)' }}\n >\n <button className=\"hub-btn\" onClick={onClose}>\n {t('common.cancel')}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CopyClientConfigDialog;\n"],"names":["normalizeDescription","description","fallback","getToolDescriptionInfo","tool","noDescriptionText","currentDescription","DEFAULT_CLIENT_SNIPPET_ID","CLIENT_SNIPPET_PRESETS","ACCESS_TOKEN_PLACEHOLDER","TOKEN_INPUT_ID","isHttpTarget","target","definedHeaders","entries","value","definedEnv","stdioEntry","args","_a","arg","env","typedTransportEntry","stdioType","headers","httpEntry","withWrappedName","wrapper","name","entry","jsonSnippet","vscodeSnippet","options","placeholder","needsTokenInput","body","TOML_BARE_KEY","tomlKey","key","tomlValue","tomlInlineTable","codexSnippet","lines","openCodeSnippet","cherryStudioSnippet","base","CLI_SAFE","shellQuote","cliArg","claudeCodeCommand","parts","asString","asServerType","asStringRecord","asStringArray","items","item","toClientSnippetTarget","config","source","configSnippet","id","buildClientSnippetBlocks","blocks","command","CopyClientConfigDialog","isOpen","onClose","useTranslation","showToast","useToast","activeId","setActiveId","useState","copiedBlock","setCopiedBlock","dialogRef","useRef","returnFocusRef","useEffect","onKeyDown","event","useMemo","handleCopy","copyText","current","jsx","jsxs","X","preset","active","block","copied","Check","Copy"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{i as b,r as l,j as s}from"./framework-vendor-CQFm9vKd.js";import{f as v,h as j,i as N}from"./index-CKByfAIo.js";import{u as f}from"./i18n-vendor-DVmjrzgW.js";import"./icons-vendor-CkcU1LMA.js";const g=({binding:a,onChange:u})=>{const{t:r}=f(),[n,m]=l.useState({}),[p,x]=l.useState(!1),[i,d]=l.useState(""),o=async t=>{x(!0),d("");try{const e=`/credentials/${encodeURIComponent(a.serverName)}`,h=t?await j(e):await N(e,{values:n});if(!h.success)throw new Error(h.message||r("credentials.failed"));m({}),d(r(t?"credentials.deleted":"credentials.saved")),await u()}catch(e){d(e instanceof Error?e.message:r("credentials.failed"))}finally{x(!1)}};return s.jsxs("form",{className:"hub-card p-5 space-y-4",onSubmit:t=>{t.preventDefault(),o(!1)},children:[s.jsxs("div",{className:"flex items-center justify-between gap-3",children:[s.jsx("h2",{className:"font-semibold",children:a.serverName}),s.jsx("span",{className:"hub-tag",children:r(a.configured?"credentials.configured":"credentials.required")})]}),s.jsx("p",{className:"text-sm text-[var(--hub-ink-3)]",children:r("credentials.writeOnly")}),a.credentialTemplate.map(t=>{var h;const e=`${t.target}.${t.name}`;return s.jsxs("label",{className:"block space-y-1 text-sm",children:[s.jsxs("span",{className:"font-medium",children:[t.label||t.name," ",s.jsxs("span",{className:"hub-mono text-xs font-normal text-[var(--hub-ink-3)]",children:["(",e,")"]})]}),t.target==="url"?s.jsxs("select",{required:!0,className:"hub-input",value:n[e]||"",onChange:c=>m({...n,[e]:c.target.value}),children:[s.jsx("option",{value:"",children:r("credentials.enterValue")}),(h=t.allowed)==null?void 0:h.map(c=>s.jsx("option",{value:c,children:c},c))]}):s.jsx("input",{type:"password",required:!0,autoComplete:"new-password",maxLength:16384,value:n[e]||"",placeholder:r(a.configuredSlots.includes(e)?"credentials.replaceValue":"credentials.enterValue"),className:"hub-input",onChange:c=>m({...n,[e]:c.target.value})})]},e)}),s.jsxs("div",{className:"flex flex-wrap gap-2",children:[s.jsx("button",{type:"submit",className:"hub-btn primary",disabled:p,children:r("credentials.save")}),s.jsx("button",{type:"button",className:"hub-btn danger",disabled:p||!a.updatedAt,onClick:()=>void o(!0),children:r("credentials.delete")})]}),i&&s.jsx("p",{role:"status",className:"text-sm text-[var(--hub-ink-2)]",children:i})]})};function k(){const{t:a}=f(),[u,r]=b(),[n,m]=l.useState([]),[p,x]=l.useState(!0),[i,d]=l.useState(""),o=l.useCallback(async()=>{try{const e=await v("/credentials");if(!e.success)throw new Error(e.message||a("credentials.failed"));m(e.data||[]),d("")}catch(e){d(e instanceof Error?e.message:a("credentials.failed"))}finally{x(!1)}},[a]);l.useEffect(()=>{o()},[o]);const t=n.filter(e=>!u.get("server")||e.serverName===u.get("server"));return s.jsxs("div",{className:"max-w-4xl mx-auto space-y-5",children:[s.jsxs("div",{children:[s.jsx("h1",{className:"hub-h1",children:a("credentials.title")}),s.jsx("p",{className:"hub-sub",children:a("credentials.description")})]}),u.has("server")&&s.jsx("button",{className:"hub-btn",onClick:()=>r({}),children:a("credentials.showAll")}),i&&s.jsx("div",{className:"hub-card px-4 py-3 text-sm",style:{color:"var(--hub-err)"},role:"alert",children:i}),p?s.jsx("div",{className:"hub-card p-10 text-center text-sm",style:{color:"var(--hub-ink-3)"},children:a("credentials.loading")}):!t.length&&!i?s.jsx("div",{className:"hub-card p-10 text-center text-sm",style:{color:"var(--hub-ink-3)"},children:a("credentials.empty")}):null,t.map(e=>s.jsx(g,{binding:e,onChange:o},e.serverName))]})}export{k as default};
2
- //# sourceMappingURL=CredentialsPage-WxYmPkS2.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CredentialsPage-WxYmPkS2.js","sources":["../../src/pages/CredentialsPage.tsx"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useSearchParams } from 'react-router-dom';\nimport type { MyCredentialBinding } from '../types';\nimport { apiDelete, apiGet, apiPut, type ApiResponse } from '../utils/fetchInterceptor';\n\nconst CredentialForm = ({\n binding,\n onChange,\n}: {\n binding: MyCredentialBinding;\n onChange: () => Promise<void>;\n}) => {\n const { t } = useTranslation();\n const [values, setValues] = useState<Record<string, string>>({});\n const [busy, setBusy] = useState(false);\n const [message, setMessage] = useState('');\n const save = async (remove: boolean) => {\n setBusy(true);\n setMessage('');\n try {\n const url = `/credentials/${encodeURIComponent(binding.serverName)}`;\n const result = remove\n ? await apiDelete<ApiResponse>(url)\n : await apiPut<ApiResponse>(url, { values });\n if (!result.success) throw new Error(result.message || t('credentials.failed'));\n setValues({});\n setMessage(t(remove ? 'credentials.deleted' : 'credentials.saved'));\n await onChange();\n } catch (error) {\n setMessage(error instanceof Error ? error.message : t('credentials.failed'));\n } finally {\n setBusy(false);\n }\n };\n return (\n <form\n className=\"hub-card p-5 space-y-4\"\n onSubmit={(event) => {\n event.preventDefault();\n void save(false);\n }}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"font-semibold\">{binding.serverName}</h2>\n <span className=\"hub-tag\">\n {t(binding.configured ? 'credentials.configured' : 'credentials.required')}\n </span>\n </div>\n <p className=\"text-sm text-[var(--hub-ink-3)]\">{t('credentials.writeOnly')}</p>\n {binding.credentialTemplate.map((slot) => {\n const id = `${slot.target}.${slot.name}`;\n return (\n <label key={id} className=\"block space-y-1 text-sm\">\n <span className=\"font-medium\">\n {slot.label || slot.name}{' '}\n <span className=\"hub-mono text-xs font-normal text-[var(--hub-ink-3)]\">({id})</span>\n </span>\n {slot.target === 'url' ? (\n <select\n required\n className=\"hub-input\"\n value={values[id] || ''}\n onChange={(event) => setValues({ ...values, [id]: event.target.value })}\n >\n <option value=\"\">{t('credentials.enterValue')}</option>\n {slot.allowed?.map((value) => (\n <option key={value} value={value}>\n {value}\n </option>\n ))}\n </select>\n ) : (\n <input\n type=\"password\"\n required\n autoComplete=\"new-password\"\n maxLength={16384}\n value={values[id] || ''}\n placeholder={t(\n binding.configuredSlots.includes(id)\n ? 'credentials.replaceValue'\n : 'credentials.enterValue',\n )}\n className=\"hub-input\"\n onChange={(event) => setValues({ ...values, [id]: event.target.value })}\n />\n )}\n </label>\n );\n })}\n <div className=\"flex flex-wrap gap-2\">\n <button type=\"submit\" className=\"hub-btn primary\" disabled={busy}>\n {t('credentials.save')}\n </button>\n <button\n type=\"button\"\n className=\"hub-btn danger\"\n disabled={busy || !binding.updatedAt}\n onClick={() => void save(true)}\n >\n {t('credentials.delete')}\n </button>\n </div>\n {message && (\n <p role=\"status\" className=\"text-sm text-[var(--hub-ink-2)]\">\n {message}\n </p>\n )}\n </form>\n );\n};\n\nexport default function CredentialsPage() {\n const { t } = useTranslation();\n const [search, setSearch] = useSearchParams();\n const [bindings, setBindings] = useState<MyCredentialBinding[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState('');\n const reload = useCallback(async () => {\n try {\n const result = await apiGet<ApiResponse<MyCredentialBinding[]>>('/credentials');\n if (!result.success) throw new Error(result.message || t('credentials.failed'));\n setBindings(result.data || []);\n setError('');\n } catch (error) {\n setError(error instanceof Error ? error.message : t('credentials.failed'));\n } finally {\n setLoading(false);\n }\n }, [t]);\n useEffect(() => {\n void reload();\n }, [reload]);\n const visible = bindings.filter(\n (binding) => !search.get('server') || binding.serverName === search.get('server'),\n );\n return (\n <div className=\"max-w-4xl mx-auto space-y-5\">\n <div>\n <h1 className=\"hub-h1\">{t('credentials.title')}</h1>\n <p className=\"hub-sub\">{t('credentials.description')}</p>\n </div>\n {search.has('server') && (\n <button className=\"hub-btn\" onClick={() => setSearch({})}>\n {t('credentials.showAll')}\n </button>\n )}\n {error && (\n <div\n className=\"hub-card px-4 py-3 text-sm\"\n style={{ color: 'var(--hub-err)' }}\n role=\"alert\"\n >\n {error}\n </div>\n )}\n {loading ? (\n <div className=\"hub-card p-10 text-center text-sm\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('credentials.loading')}\n </div>\n ) : !visible.length && !error ? (\n <div className=\"hub-card p-10 text-center text-sm\" style={{ color: 'var(--hub-ink-3)' }}>\n {t('credentials.empty')}\n </div>\n ) : null}\n {visible.map((binding) => (\n <CredentialForm key={binding.serverName} binding={binding} onChange={reload} />\n ))}\n </div>\n );\n}\n"],"names":["CredentialForm","binding","onChange","t","useTranslation","values","setValues","useState","busy","setBusy","message","setMessage","save","remove","url","result","apiDelete","apiPut","error","jsxs","event","jsx","slot","id","_a","value","CredentialsPage","search","setSearch","useSearchParams","bindings","setBindings","loading","setLoading","setError","reload","useCallback","apiGet","useEffect","visible"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{r as y,j as o}from"./framework-vendor-CQFm9vKd.js";import{j as h,n as r}from"./index-CKByfAIo.js";import{c as x}from"./clipboard-CoLstiwP.js";import{u as C}from"./i18n-vendor-DVmjrzgW.js";import{C as b,r as j}from"./icons-vendor-CkcU1LMA.js";const g=({url:s,label:e,prefix:a,className:p,copyValue:m,ariaLabel:l})=>{const{t}=C(),{showToast:n}=h(),[c,i]=y.useState(!1),d=async u=>{if(u.stopPropagation(),!await x(m??s)){n(t("common.copyFailed")||"Copy failed","error");return}i(!0),n(t("common.copySuccess")||"Copied to clipboard","success"),setTimeout(()=>i(!1),1200)};return o.jsxs("div",{className:r("hub-endpoint",p),role:"group","aria-label":l||s,children:[e&&o.jsx("div",{className:"hub-endpoint-label",children:e}),o.jsxs("div",{className:"hub-endpoint-url",title:s,children:[a&&o.jsx("span",{style:{color:"var(--hub-ink-3)"},children:a}),s]}),o.jsx("button",{type:"button",onClick:d,className:r("hub-endpoint-copy",c?"copied":""),title:t("common.copy")||"Copy","aria-label":t("common.copy")||"Copy",children:c?o.jsx(b,{size:13}):o.jsx(j,{size:13})})]})};export{g as E};
2
- //# sourceMappingURL=EndpointCopy-DxCJY8u6.js.map
@@ -1,33 +0,0 @@
1
- import{r as g,j as e,R as M}from"./framework-vendor-CQFm9vKd.js";import{n as Z,f as fe,d as ee,u as ve,j as je,k as se}from"./index-CKByfAIo.js";import{u as te,a as re}from"./useCostData-Br7dI3R9.js";import{u as G}from"./i18n-vendor-DVmjrzgW.js";import{u as me,c as ye}from"./clipboard-CoLstiwP.js";import{f as H,p as Ne}from"./contextCost-CilWqGWT.js";import{g as ke,A as Se,C as we}from"./CopyClientConfigDialog-Clr8hk1S.js";import{W as Ce,g as Ae,F as Te,H as Ee,C as De,r as Q,J as oe,z as Ie,w as Fe,T as ze,n as Pe,D as ce,N as Re,P as de,l as Le,X as Ue}from"./icons-vendor-CkcU1LMA.js";import{D as $e}from"./DeleteDialog-BaCsEkJA.js";const Oe=["read","create","update","delete","other"],Ge=a=>{var s,l,h;if(((s=a.annotations)==null?void 0:s.readOnlyHint)===!0)return"read";const m=a.value.toLowerCase();return/^(delete|remove)[_-]/.test(m)?"delete":((l=a.annotations)==null?void 0:l.destructiveHint)===!0?"other":/^(create|add)[_-]/.test(m)?"create":/^(update|set)[_-]/.test(m)?"update":((h=a.annotations)==null?void 0:h.readOnlyHint)!==!1&&/^(get|list|read|search)[_-]/.test(m)?"read":"other"},We=(a,m,s)=>{const l=m.trim().toLowerCase();return a.filter(h=>{var i,p;return(h.value.toLowerCase().includes(l)||((i=h.description)==null?void 0:i.toLowerCase().includes(l)))&&(s==="all"||((p=h.annotations)==null?void 0:p[s])===!0)})},Me=(a,m,s,l)=>{if(s.length===0||a==="all"&&l==="select")return a;const h=new Set(a==="all"?m:a);for(const i of s)l==="deselect"||l==="invert"&&h.has(i)?h.delete(i):h.add(i);return[...h]},He=({items:a,selection:m,tools:s,label:l,onChange:h,children:i})=>{const{t:p}=G(),[E,I]=g.useState(""),[N,A]=g.useState("all"),[x,w]=g.useState(!1),j=We(a,E,N),T=new Set(m==="all"?a.map(t=>t.value):m),d=(t,o)=>h(Me(m,a.map(c=>c.value),t.map(c=>c.value),o)),b=s&&x?Oe.map(t=>({risk:t,items:j.filter(o=>Ge(o)===t)})):[{risk:null,items:j}],f="text-xs text-blue-600 hover:text-blue-800 disabled:opacity-40 disabled:cursor-not-allowed";return e.jsxs("section",{"aria-label":l,children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2 mb-2",children:[e.jsx("input",{type:"search","aria-label":p("groups.bulkSearchLabel",{capability:l}),placeholder:p("groups.bulkSearch"),value:E,onChange:t=>I(t.target.value),className:"min-w-0 flex-1 basis-48 rounded border border-gray-300 bg-white dark:bg-gray-800 px-2 py-1 text-sm"}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("select",{"aria-label":p("groups.annotationFilter"),value:N,onChange:t=>A(t.target.value),className:"max-w-full rounded border border-gray-300 bg-white dark:bg-gray-800 px-2 py-1 text-sm",children:[e.jsx("option",{value:"all",children:p("groups.allAnnotations")}),["readOnlyHint","destructiveHint","idempotentHint"].map(t=>e.jsx("option",{value:t,children:p(`groups.${t}`)},t))]}),e.jsx("button",{type:"button","aria-pressed":x,onClick:()=>w(!x),className:f,children:p(x?"groups.flatList":"groups.riskGroups")})]})]}),e.jsxs("div",{className:"flex flex-wrap items-center gap-x-3 gap-y-1 mb-2",children:[e.jsx("span",{className:"text-xs text-gray-500",role:"status",children:p("groups.visibleCount",{count:j.length,total:a.length})}),["select","deselect","invert"].map(t=>e.jsx("button",{type:"button",disabled:!j.length,onClick:()=>d(j,t),className:f,children:p(`groups.${t}Visible`)},t))]}),s&&x&&e.jsx("p",{className:"text-xs text-gray-500 mb-2",children:p("groups.riskHint")}),e.jsxs("div",{className:"grid grid-cols-1 gap-2 max-h-96 overflow-y-auto",children:[j.length===0&&e.jsx("p",{className:"text-sm text-gray-500",children:p("groups.noMatchingItems")}),b.filter(t=>t.items.length>0).map(t=>{const o=t.items.filter(c=>T.has(c.value)).length;return e.jsxs(M.Fragment,{children:[t.risk&&e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2 border-b border-gray-200 py-1",children:[e.jsxs("span",{className:"text-xs font-medium",children:[p(`groups.risk_${t.risk}`)," (",o,"/",t.items.length,")"]}),e.jsx("button",{type:"button",className:f,onClick:()=>d(t.items,o===t.items.length?"deselect":"select"),children:p(o===t.items.length?"groups.selectNone":"groups.selectAll")})]}),t.items.map(c=>e.jsx(M.Fragment,{children:i(c)},c.value))]},t.risk??"flat")})]})]})},_e={tools:[],prompts:[],resources:[]},ue={tools:"all",prompts:"all",resources:"all"},pe=({servers:a,value:m,onChange:s,className:l,serverCosts:h=[]})=>{const{t:i}=G(),p=M.useId(),{nameSeparator:E,smartRoutingConfig:I}=me(),[N,A]=g.useState(new Set),x=M.useMemo(()=>m.map(r=>typeof r=="string"?{name:r,...ue}:{...r,tools:r.tools||"all",prompts:r.prompts||"all",resources:r.resources||"all"}),[m]),w=M.useMemo(()=>a.filter(r=>r.enabled!==!1),[a]);M.useEffect(()=>{const r=new Set(x.map(n=>n.name)),u=new Set(w.map(n=>n.name));A(n=>{const S=new Set;return n.forEach(v=>{(r.has(v)||u.has(v))&&S.add(v)}),S})},[x,w]);const j=r=>{if(x.findIndex(n=>n.name===r)>=0){const n=x.filter(S=>S.name!==r);s(n)}else{const n=[...x,{name:r,...ue}];s(n)}},T=r=>{A(u=>{const n=new Set(u);return n.has(r)?n.delete(r):n.add(r),n})},d=r=>["tools","prompts","resources"].some(u=>{const n=r[u];return n==="all"||Array.isArray(n)&&n.length>0}),b=(r,u,n,S=!1)=>{const v=x.find(k=>k.name===r),F={...v?{...v}:{name:r,..._e},[u]:n};if(u==="tools"&&Array.isArray(n)&&F.pinnedTools&&(F.pinnedTools=F.pinnedTools.filter(k=>n.includes(k)),F.pinnedTools.length===0&&delete F.pinnedTools),!d(F)){const k=x.filter(y=>y.name!==r);s(k),S||A(y=>{const $=new Set(y);return $.delete(r),$});return}if(v){s(x.map(k=>k.name===r?F:k));return}s([...x,F])},f=(r,u)=>{const n=x.find(v=>v.name===r);if(!n)return;const S={...n};u?S.alias=u:delete S.alias,s(x.map(v=>v.name===r?S:v))},t=(r,u)=>{const n=x.find(P=>P.name===r);if(!n)return;const S=n.pinnedTools??[],v={...n,pinnedTools:S.includes(u)?S.filter(P=>P!==u):[...S,u]};v.pinnedTools.length===0&&delete v.pinnedTools,s(x.map(P=>P.name===r?v:P))},o=(r,u)=>{const n=`${r}${E}`;return u.startsWith(n)?u.slice(n.length):u},c=(r,u)=>u==="tools"?(r.tools||[]).filter(n=>n.enabled!==!1).map(n=>({key:n.name,value:o(r.name,n.name),description:n.description,annotations:n.annotations,defaultDescription:n.defaultDescription,hasDescriptionOverride:n.hasDescriptionOverride})):u==="prompts"?(r.prompts||[]).filter(n=>n.enabled!==!1).map(n=>({key:n.name,value:o(r.name,n.name),description:n.description})):(r.resources||[]).filter(n=>n.enabled!==!1).map(n=>({key:n.uri,value:n.uri,description:n.description})),C=M.useMemo(()=>{const r=new Map;return h.forEach(u=>{const n=new Map;u.items.forEach(S=>n.set(S.name,S.cost)),r.set(u.name,n)}),r},[h]),z=r=>C.get(r)??new Map,O=(r,u)=>{const n=z(r.name);return c(r,u).filter(S=>J(r.name,u,S.value)).reduce((S,v)=>S+(n.get(v.key)??0),0)},_=r=>["tools","prompts","resources"].reduce((u,n)=>u+O(r,n),0),X=(r,u,n)=>{const S=w.find(k=>k.name===r);if(!S)return;const v=c(S,u).map(k=>k.value),P=x.find(k=>k.name===r);if(!P){b(r,u,[n]);return}const F=P[u];if(F==="all"){const k=v.filter(y=>y!==n);b(r,u,k);return}if(Array.isArray(F)){if(F.includes(n)){b(r,u,F.filter(y=>y!==n));return}const k=[...F,n];b(r,u,k.length===v.length?"all":k);return}b(r,u,[n])},W=r=>{const u=x.find(n=>n.name===r);return!!(u&&d(u))},K=r=>{const u=x.find(n=>n.name===r);return u?["tools","prompts","resources"].some(n=>{const S=u[n];return Array.isArray(S)&&S.length>0}):!1},J=(r,u,n)=>{const S=x.find(P=>P.name===r);if(!S)return!1;const v=S[u];return v==="all"?!0:Array.isArray(v)?v.includes(n):!1},B=(r,u)=>{const n=x.find(P=>P.name===r.name);if(!n)return 0;const S=c(r,u),v=n[u];if(v==="all")return S.length;if(Array.isArray(v)){const P=new Set(S.map(F=>F.value));return v.filter(F=>P.has(F)).length}return 0},D=[{key:"tools",titleKey:"groups.toolSelection",countKey:"groups.toolsSelected",allKey:"groups.allTools"},{key:"prompts",titleKey:"groups.promptSelection",countKey:"groups.promptsSelected",allKey:"groups.allPrompts"},{key:"resources",titleKey:"groups.resourceSelection",countKey:"groups.resourcesSelected",allKey:"groups.allResources"}],R=r=>D.map(({key:u})=>({key:u,count:B(r,u)})).filter(u=>u.count>0);return e.jsxs("div",{className:Z("space-y-4",l),children:[e.jsx("div",{className:"space-y-3",children:w.map(r=>{const u=W(r.name),n=K(r.name),S=N.has(r.name),v=x.find(y=>y.name===r.name),P=R(r),F=D.filter(({key:y})=>c(r,y).length>0),k=z(r.name);return e.jsxs("div",{className:"border border-[var(--hub-line)] rounded-lg bg-[var(--hub-surface)] hover:border-[var(--hub-ink-3)] hover:bg-[var(--hub-surface-hover)] transition-colors",children:[e.jsxs("div",{className:"flex items-center justify-between p-3 cursor-pointer rounded-lg transition-colors",onClick:()=>T(r.name),children:[e.jsxs("div",{className:"flex items-center space-x-3",onClick:y=>{y.stopPropagation(),j(r.name)},children:[e.jsx("input",{type:"checkbox",checked:u||n,onChange:()=>j(r.name),className:"w-4 h-4 text-blue-600 accent-[var(--hub-accent)] rounded focus:ring-blue-500"}),e.jsx("span",{className:"font-medium text-[var(--hub-ink)] cursor-pointer select-none",children:r.name})]}),e.jsxs("div",{className:"flex items-center space-x-3",children:[_(r)>0&&e.jsxs("span",{className:"text-sm text-[var(--hub-ink-3)] hub-mono",title:i("cost.estimate"),children:["Σ ",H(_(r))]}),P.map(({key:y,count:$})=>e.jsxs("span",{className:"text-sm text-green-600 flex items-center gap-1",children:[y==="tools"?e.jsx(Ce,{size:14}):y==="prompts"?e.jsx(Ae,{size:14}):e.jsx(Te,{size:14})," ",$]},y)),F.length>0&&e.jsx("button",{type:"button",className:"p-1 text-[var(--hub-ink-3)] hover:text-[var(--hub-ink-2)] transition-colors",children:e.jsx("svg",{className:Z("w-5 h-5 transition-transform",S&&"rotate-180"),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M19 9l-7 7-7-7"})})})]})]}),S&&F.length>0&&e.jsx("div",{className:"border-t border-[var(--hub-line)] bg-[var(--hub-bg-2)] p-3",children:e.jsxs("div",{className:"space-y-4",children:[v&&e.jsxs("div",{className:"space-y-1",onClick:y=>y.stopPropagation(),children:[e.jsx("label",{htmlFor:`${p}-${r.name}`,className:"block text-xs font-medium text-[var(--hub-ink-2)]",children:i("groups.alias")}),e.jsx("input",{type:"text",id:`${p}-${r.name}`,value:v.alias||"",placeholder:r.name,onChange:y=>f(r.name,y.target.value),className:"hub-input"}),e.jsxs("label",{className:"flex items-center gap-2 text-xs text-[var(--hub-ink-2)]",children:[e.jsx("input",{type:"checkbox",checked:v.preserveToolNames===!0,onChange:y=>s(x.map($=>$.name===r.name?{...$,preserveToolNames:y.target.checked}:$))}),i("groups.preserveToolNames")]}),e.jsx("p",{className:"text-xs text-[var(--hub-ink-3)]",children:i("groups.preserveToolNamesHint")})]}),F.map(({key:y,titleKey:$,countKey:be,allKey:ge})=>{const V=c(r,y),ae=B(r,y),Y=(v==null?void 0:v[y])==="all"||ae===V.length;return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-3",children:[e.jsx("span",{className:"text-sm font-medium text-[var(--hub-ink-2)]",children:i($)}),e.jsxs("div",{className:"flex items-center gap-3",children:[v&&e.jsx("span",{className:"text-xs text-green-600",children:Y?`(${i(ge)} ${V.length}/${V.length})`:`(${i(be)} ${ae}/${V.length})`}),v&&O(r,y)>0&&e.jsxs("span",{className:"text-xs text-[var(--hub-ink-3)] hub-mono",title:i("cost.estimate"),children:["Σ ",H(O(r,y))]}),e.jsx("button",{type:"button",onClick:()=>{b(r.name,y,Y?[]:"all",!0)},className:"text-sm text-blue-600 hover:text-blue-800 transition-colors",children:i(Y?"groups.selectNone":"groups.selectAll")})]})]}),e.jsx(He,{items:V,selection:(v==null?void 0:v[y])??[],tools:y==="tools",label:i($),onChange:L=>b(r.name,y,L,!0),children:L=>{var ie;const ne=J(r.name,y,L.value),q=!!((ie=v==null?void 0:v.pinnedTools)!=null&&ie.includes(L.value)),U=y==="tools"?ke({description:L.description,defaultDescription:L.defaultDescription,hasDescriptionOverride:L.hasDescriptionOverride},i("tool.noDescription")):null,le=U!=null&&U.hasDescriptionOverride?i("tool.defaultDescriptionTooltip",{description:U.defaultDescription}):L.description;return e.jsxs("div",{className:"flex min-w-0 items-center gap-2",children:[e.jsxs("label",{className:"flex min-w-0 flex-1 items-center gap-2 text-sm",children:[e.jsx("input",{type:"checkbox",checked:ne,onChange:()=>X(r.name,y,L.value),className:"w-3 h-3 text-blue-600 accent-[var(--hub-accent)] rounded focus:ring-blue-500"}),e.jsx("span",{className:"text-[var(--hub-ink-2)] break-all whitespace-nowrap flex-shrink-0",children:L.value}),(L.description||(U==null?void 0:U.hasDescriptionOverride))&&e.jsxs("span",{className:"min-w-0 flex items-center gap-1 text-[var(--hub-ink-3)] text-xs truncate",children:[e.jsx("span",{className:"truncate",title:le||void 0,children:U?U.currentDescription:L.description}),(U==null?void 0:U.hasDescriptionOverride)&&e.jsx("span",{className:"inline-flex flex-shrink-0 items-center rounded-full border border-amber-200 bg-amber-50 px-1.5 py-0.5 text-[10px] font-medium text-amber-700 dark:border-amber-700/60 dark:bg-amber-900/20 dark:text-amber-300",title:le||void 0,children:i("tool.descriptionModifiedBadge")})]}),k.get(L.key)!=null&&e.jsxs("span",{className:"text-xs text-[var(--hub-ink-3)] hub-mono whitespace-nowrap ml-auto flex-shrink-0",title:i("cost.estimate"),children:["Σ ",H(k.get(L.key))]})]}),y==="tools"&&ne&&(I.enabled||q)&&e.jsx("button",{type:"button",onClick:()=>t(r.name,L.value),title:i(q?"groups.unpinTool":"groups.pinTool"),"aria-label":i("groups.pinToolLabel"),"aria-pressed":q,className:Z("flex-shrink-0 transition-colors",q?"text-blue-600":"text-[var(--hub-ink-3)] hover:text-[var(--hub-ink-2)]"),children:e.jsx(Ee,{size:12,fill:q?"currentColor":"none"})})]},L.key)}})]},y)})]})})]},r.name)})}),w.length===0&&e.jsx("p",{className:"text-[var(--hub-ink-3)] text-sm",children:i("groups.noServerOptions")})]})};function xe({groupId:a,value:m,onChange:s}){const{t:l}=G(),[h,i]=g.useState([]),[p,E]=g.useState(""),[I,N]=g.useState(!1),[A,x]=g.useState(!1),[w,j]=g.useState(!1);g.useEffect(()=>{if(!a||m.visibility!=="group")return;let c=!1;return N(!0),x(!1),fe(`/groups/${encodeURIComponent(a)}/share-candidates`).then(C=>{c||(C.success&&Array.isArray(C.data)?i(C.data):x(!0))}).catch(()=>{c||x(!0)}).finally(()=>{c||N(!1)}),()=>{c=!0}},[a,m.visibility]);const T=m.sharedWithUsers||[],d=[...new Set([...h,...T])].sort().filter(c=>c.toLowerCase().includes(p.toLowerCase())),b=new Set(T),f=d.length>0&&d.every(c=>b.has(c)),t=d.length===0||d.every(c=>!b.has(c)),o=c=>{const C=new Set(T);C.has(c)?C.delete(c):C.add(c),s({sharedWithUsers:Array.from(C)})};return e.jsxs("div",{className:"mb-4",children:[e.jsxs("div",{className:"flex items-center justify-between cursor-pointer bg-[var(--hub-bg-2)] hover:bg-[var(--hub-surface-hover)] p-3 rounded border border-[var(--hub-line)]",onClick:()=>j(!w),children:[e.jsx("h3",{className:"text-sm font-semibold text-[var(--hub-ink)]",children:l("server.sectionAdvanced","Advanced Options")}),e.jsx("span",{className:"text-[var(--hub-ink-3)] text-sm",children:w?"▼":"▶"})]}),w&&e.jsxs("div",{className:"border border-[var(--hub-line)] rounded-b p-4 bg-[var(--hub-surface)] border-t-0",children:[e.jsx("label",{htmlFor:"group-visibility",className:"block text-sm font-medium mb-1.5 text-[var(--hub-ink-2)]",children:l("server.visibility")}),e.jsxs("select",{id:"group-visibility",className:"hub-input",value:m.visibility||"",onChange:c=>s({visibility:c.target.value}),children:[!m.visibility&&e.jsx("option",{value:"",disabled:!0,children:l("groups.legacyVisibility")}),e.jsx("option",{value:"private",children:l("server.visibilityPrivate")}),e.jsx("option",{value:"group",children:l("server.visibilityGroup")}),e.jsx("option",{value:"public",children:l("server.visibilityPublic")})]}),e.jsx("p",{className:"text-xs text-[var(--hub-ink-3)] mt-1",children:l("groups.visibilityDescription")}),!m.visibility&&e.jsx("p",{className:"text-sm text-amber-600",children:l("groups.legacyVisibilityHint")}),m.visibility==="group"&&e.jsxs("div",{className:"mt-4 rounded border border-[var(--hub-line)] p-3",children:[e.jsx("div",{className:"text-sm font-medium text-[var(--hub-ink-2)]",children:l("server.shareWithUsers")}),e.jsx("p",{className:"text-xs text-[var(--hub-ink-3)] mt-1 mb-3",children:l("server.shareWithUsersDescription")}),a?e.jsxs(e.Fragment,{children:[I&&e.jsx("p",{className:"text-sm text-[var(--hub-ink-3)]",children:l("server.shareCandidatesLoading")}),A&&e.jsx("p",{role:"alert",className:"text-sm text-red-600 dark:text-red-400",children:l("server.shareCandidatesError")}),!I&&!A&&d.length===0&&e.jsx("p",{className:"text-sm text-[var(--hub-ink-3)]",children:l(p?"server.noMatchingShareUsers":"server.noShareCandidates")}),d.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 space-y-2",children:[e.jsx("label",{htmlFor:"group-share-search",className:"block text-xs font-medium text-[var(--hub-ink-2)]",children:l("server.shareUserSearchLabel")}),e.jsx("input",{id:"group-share-search",type:"search",value:p,onChange:c=>E(c.target.value),placeholder:l("server.shareUserSearchPlaceholder"),className:"hub-input text-sm"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx("button",{type:"button",onClick:()=>s({sharedWithUsers:[...new Set([...T,...d])]}),disabled:f,className:"hub-btn text-sm",children:l("server.selectAllShareUsers")}),e.jsx("button",{type:"button",onClick:()=>s({sharedWithUsers:T.filter(c=>!d.includes(c))}),disabled:t,className:"hub-btn text-sm",children:l("server.deselectAllShareUsers")})]})]}),e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-2",children:d.map(c=>e.jsxs("label",{className:"flex items-center gap-2 rounded border border-[var(--hub-line)] px-3 py-2 text-sm text-[var(--hub-ink-2)]",children:[e.jsx("input",{type:"checkbox",checked:b.has(c),onChange:()=>o(c)}),e.jsx("span",{children:c})]},c))})]})]}):e.jsx("p",{className:"text-sm text-[var(--hub-ink-3)]",children:l("groups.shareAfterCreate")})]})]})]})}const Be=({onAdd:a,onCancel:m})=>{const{t:s}=G(),{createGroup:l}=ee(),{allServers:h}=te(),{serverCosts:i}=re(),[p,E]=g.useState([]),[I,N]=g.useState(null),[A,x]=g.useState(!1),[w,j]=g.useState({visibility:"private",sharedWithUsers:[],name:"",description:"",instructions:"",servers:[]});g.useEffect(()=>{E(h.filter(b=>b.enabled!==!1))},[h]);const T=b=>{const{name:f,value:t}=b.target;j(o=>({...o,[f]:t}))},d=async b=>{b.preventDefault(),x(!0),N(null);try{if(!w.name.trim()){N(s("groups.nameRequired")),x(!1);return}const f=await l(w.name,w.description,w.servers,{visibility:w.visibility,sharedWithUsers:w.sharedWithUsers,instructions:w.instructions});if(!f||!f.success){N((f==null?void 0:f.message)||s("groups.createError")),x(!1);return}a()}catch(f){N(f instanceof Error?f.message:String(f)),x(!1)}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"hub-card shadow-lg max-w-3xl w-full max-h-[90vh] flex flex-col",children:[e.jsxs("div",{className:"p-6 flex-shrink-0",children:[e.jsx("h2",{className:"text-xl font-semibold text-[var(--hub-ink)] mb-4",children:s("groups.addNew")}),I&&e.jsx("div",{className:"mb-4 p-3 bg-red-100 text-red-700 rounded-md border border-[var(--hub-line)]",children:I})]}),e.jsxs("form",{onSubmit:d,className:"flex flex-col flex-1 min-h-0",children:[e.jsx("div",{className:"flex-1 overflow-y-auto px-6",children:e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[var(--hub-ink-2)] text-sm font-bold mb-2",htmlFor:"name",children:[s("groups.name")," *"]}),e.jsx("input",{type:"text",id:"name",name:"name",value:w.name,onChange:T,className:"w-full border border-[var(--hub-line)] bg-[var(--hub-surface)] rounded-md px-3 py-2 text-[var(--hub-ink)] placeholder:text-[var(--hub-ink-3)] focus:outline-none focus:border-[var(--hub-ink-2)]",placeholder:s("groups.namePlaceholder"),required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[var(--hub-ink-2)] text-sm font-bold mb-2",htmlFor:"instructions",children:s("groups.instructions")}),e.jsx("textarea",{id:"instructions",name:"instructions",value:w.instructions,onChange:T,rows:3,className:"w-full border border-[var(--hub-line)] bg-[var(--hub-surface)] rounded-md px-3 py-2 text-[var(--hub-ink)] placeholder:text-[var(--hub-ink-3)] focus:outline-none focus:border-[var(--hub-ink-2)]",placeholder:s("groups.instructionsPlaceholder")}),e.jsx("p",{className:"text-xs text-[var(--hub-ink-3)] mt-1",children:s("groups.instructionsHelp")})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[var(--hub-ink-2)] text-sm font-bold mb-2",children:s("groups.configureCapabilities")}),e.jsx(pe,{servers:p,value:w.servers,onChange:b=>j(f=>({...f,servers:b})),className:"border border-[var(--hub-line)] rounded-lg p-4 bg-[var(--hub-bg-2)]",serverCosts:i})]}),e.jsx(xe,{value:w,onChange:b=>j(f=>({...f,...b}))})]})}),e.jsxs("div",{className:"flex justify-end space-x-2 p-5 pt-3 border-t border-[var(--hub-line-2)] flex-shrink-0",children:[e.jsx("button",{type:"button",onClick:m,className:"hub-btn",disabled:A,children:s("common.cancel")}),e.jsx("button",{type:"submit",className:"hub-btn primary",disabled:A,children:s(A?"common.submitting":"common.create")})]})]})]})})},Ve=({group:a,onEdit:m,onCancel:s})=>{const{t:l}=G(),{updateGroup:h}=ee(),{allServers:i}=te(),{serverCosts:p}=re(),[E,I]=g.useState([]),[N,A]=g.useState(null),[x,w]=g.useState(!1),[j,T]=g.useState({visibility:a.visibility,sharedWithUsers:a.sharedWithUsers||[],name:a.name,description:a.description||"",instructions:a.instructions||"",servers:a.servers||[]});g.useEffect(()=>{I(i.filter(f=>f.enabled!==!1))},[i]);const d=f=>{const{name:t,value:o}=f.target;T(c=>({...c,[t]:o}))},b=async f=>{f.preventDefault(),w(!0),A(null);try{if(!j.name.trim()){A(l("groups.nameRequired")),w(!1);return}const t=await h(a.id,{visibility:j.visibility,sharedWithUsers:j.sharedWithUsers,name:j.name,description:j.description,instructions:j.instructions,servers:j.servers});if(!t||!t.success){A((t==null?void 0:t.message)||l("groups.updateError")),w(!1);return}m()}catch(t){A(t instanceof Error?t.message:String(t)),w(!1)}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"hub-card shadow-lg max-w-3xl w-full max-h-[90vh] flex flex-col",children:[e.jsxs("div",{className:"p-6 flex-shrink-0",children:[e.jsx("h2",{className:"text-xl font-semibold text-[var(--hub-ink)] mb-4",children:l("groups.edit")}),N&&e.jsx("div",{className:"mb-4 p-3 bg-red-100 text-red-700 rounded-md border border-[var(--hub-line)]",children:N})]}),e.jsxs("form",{onSubmit:b,className:"flex flex-col flex-1 min-h-0",children:[e.jsx("div",{className:"flex-1 overflow-y-auto px-6",children:e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-[var(--hub-ink-2)] text-sm font-bold mb-2",htmlFor:"name",children:[l("groups.name")," *"]}),e.jsx("input",{type:"text",id:"name",name:"name",value:j.name,onChange:d,className:"w-full border border-[var(--hub-line)] bg-[var(--hub-surface)] rounded-md px-3 py-2 text-[var(--hub-ink)] placeholder:text-[var(--hub-ink-3)] focus:outline-none focus:border-[var(--hub-ink-2)]",placeholder:l("groups.namePlaceholder"),required:!0})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[var(--hub-ink-2)] text-sm font-bold mb-2",htmlFor:"instructions",children:l("groups.instructions")}),e.jsx("textarea",{id:"instructions",name:"instructions",value:j.instructions,onChange:d,rows:3,className:"w-full border border-[var(--hub-line)] bg-[var(--hub-surface)] rounded-md px-3 py-2 text-[var(--hub-ink)] placeholder:text-[var(--hub-ink-3)] focus:outline-none focus:border-[var(--hub-ink-2)]",placeholder:l("groups.instructionsPlaceholder")}),e.jsx("p",{className:"text-xs text-[var(--hub-ink-3)] mt-1",children:l("groups.instructionsHelp")})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[var(--hub-ink-2)] text-sm font-bold mb-2",children:l("groups.configureCapabilities")}),e.jsx(pe,{servers:E,value:j.servers,onChange:f=>T(t=>({...t,servers:f})),className:"border border-[var(--hub-line)] rounded-lg p-4 bg-[var(--hub-bg-2)]",serverCosts:p})]}),e.jsx(xe,{groupId:a.id,value:j,onChange:f=>T(t=>({...t,...f}))})]})}),e.jsxs("div",{className:"flex justify-end space-x-2 p-5 pt-3 border-t border-[var(--hub-line-2)] flex-shrink-0",children:[e.jsx("button",{type:"button",onClick:s,className:"hub-btn",disabled:x,children:l("common.cancel")}),e.jsx("button",{type:"submit",className:"hub-btn primary",disabled:x,children:l(x?"common.submitting":"common.save")})]})]})]})})},qe=a=>a.map(m=>typeof m=="string"?m:m.name),he=(a,m)=>{const s=a.servers.find(l=>typeof l=="string"?l===m:l.name===m);return s?typeof s=="string"?{name:s,tools:"all",prompts:"all",resources:"all"}:s:{name:m,tools:"all",prompts:"all",resources:"all"}},Ke=(a,m)=>{var l;return((l=he(a,m).alias)==null?void 0:l.trim())||m},Je=({group:a,servers:m,onEdit:s,onDelete:l,cost:h})=>{var B;const{t:i}=G(),{auth:p}=ve(),E=!!p.user&&(p.user.isAdmin||p.user.username===a.owner),{showToast:I}=je(),{installConfig:N,nameSeparator:A}=me(),x=((B=N==null?void 0:N.baseUrl)==null?void 0:B.replace(/\/+$/,""))||"",[w,j]=g.useState(!1),[T,d]=g.useState(null),[b,f]=g.useState(!1),[t,o]=g.useState(!1),c=g.useRef(null);g.useEffect(()=>{const D=R=>{c.current&&!c.current.contains(R.target)&&o(!1)};return document.addEventListener("mousedown",D),()=>document.removeEventListener("mousedown",D)},[]);const C=async D=>{await ye(D)?(f(!0),o(!1),I(i("common.copySuccess")||"Copied","success"),setTimeout(()=>f(!1),1500)):I(i("common.copyFailed")||"Copy failed","error")},z=`${x}/mcp/${a.name}`,O=`${x}/mcp/${encodeURIComponent(a.name)}`,_=`${x}/api/${a.name}`,X=qe(a.servers),W=m.filter(D=>X.includes(D.name)),K=D=>{const R=he(a,D.name),r=`${D.name}${A}`,u=D.tools||[],n=D.prompts||[],S=D.resources||[],v=Array.isArray(R.tools)?u.filter(k=>{if(k.enabled===!1)return!1;const y=k.name.startsWith(r)?k.name.slice(r.length):k.name;return R.tools.includes(y)}).length:u.filter(k=>k.enabled!==!1).length,P=Array.isArray(R.prompts)?n.filter(k=>{if(k.enabled===!1)return!1;const y=k.name.startsWith(r)?k.name.slice(r.length):k.name;return R.prompts.includes(y)}).length:n.filter(k=>k.enabled!==!1).length,F=Array.isArray(R.resources)?S.filter(k=>k.enabled!==!1&&R.resources.includes(k.uri)).length:S.filter(k=>k.enabled!==!1).length;return{visibleTools:v,totalTools:u.length,visiblePrompts:P,totalPrompts:n.length,visibleResources:F,totalResources:S.length}},J=W.reduce((D,R)=>D+K(R).visibleTools,0);return e.jsxs("div",{className:"hub-card flex h-full flex-col overflow-visible",children:[e.jsxs("div",{className:"flex items-start gap-3 px-4 py-3",style:{borderBottom:"1px solid var(--hub-line-2)"},children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{style:{fontSize:15,fontWeight:600,letterSpacing:"-0.015em"},children:a.name}),e.jsx("span",{className:"hub-mono",style:{fontSize:11,color:"var(--hub-ink-3)",padding:"0 6px",border:"1px solid var(--hub-line)",borderRadius:4,height:18,display:"inline-flex",alignItems:"center"},title:a.id,children:a.id})]}),e.jsx("div",{className:"mt-1 text-xs text-[var(--hub-ink-3)]",children:a.visibility?i(`server.visibility${a.visibility==="group"?"Group":a.visibility==="public"?"Public":"Private"}`):i("groups.legacyVisibility")}),a.description&&e.jsx("div",{style:{fontSize:12.5,color:"var(--hub-ink-3)",marginTop:2},children:a.description})]}),e.jsxs("div",{className:"flex items-center gap-1",ref:c,children:[e.jsxs("div",{className:"relative",children:[e.jsx("button",{onClick:()=>o(D=>!D),className:"hub-icon-btn sm",title:i("common.copy"),children:b?e.jsx(De,{size:13,className:"text-[var(--hub-ok)]"}):e.jsx(Q,{size:13})}),t&&e.jsxs("div",{className:"absolute top-full right-0 mt-1 z-20 hub-card",style:{minWidth:160,padding:4},children:[e.jsxs("button",{onClick:()=>C(a.id),className:"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left",children:[e.jsx(Q,{size:12})," ",i("common.copyId")]}),e.jsxs("button",{onClick:()=>C(z),className:"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left",children:[e.jsx(oe,{size:12})," ",i("common.copyUrl")]}),e.jsxs("button",{onClick:()=>C(_),className:"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left",children:[e.jsx(oe,{size:12})," ",i("common.copyOpenApiUrl")]}),e.jsxs("button",{onClick:()=>{o(!1),d({name:a.name,url:O,headers:{Authorization:`Bearer ${Se}`}})},className:"flex items-center gap-2 w-full px-2.5 py-1.5 text-[13px] rounded-md hover:bg-[var(--hub-surface-hover)] text-left",children:[e.jsx(Ie,{size:12})," ",i("clientConfig.menuItem")]})]})]}),E&&e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:()=>s(a),className:"hub-icon-btn sm",title:i("groups.edit"),children:e.jsx(Fe,{size:13})}),e.jsx("button",{onClick:()=>j(!0),className:"hub-icon-btn sm",title:i("groups.delete"),style:{color:"var(--hub-ink-3)"},children:e.jsx(ze,{size:13})})]})]})]}),e.jsxs("div",{className:"grid flex-1 items-center gap-3 px-4 py-3",style:{gridTemplateColumns:"1fr 80px 1fr",minHeight:180},children:[e.jsx("div",{className:"flex flex-col gap-1.5",children:W.length===0?e.jsx("div",{style:{fontSize:12,color:"var(--hub-ink-3)"},children:i("groups.noServers")}):W.map(D=>{const R=K(D);return e.jsxs("div",{className:"flex items-center gap-2.5 px-2.5 py-1.5 rounded-md",style:{background:"var(--hub-bg-2)",border:"1px solid var(--hub-line-2)"},children:[e.jsx("span",{className:"inline-block flex-shrink-0",style:{width:6,height:6,borderRadius:50,background:D.status==="connected"?"var(--hub-ok)":D.status==="connecting"?"var(--hub-warn)":"var(--hub-err)"}}),e.jsx("span",{className:"hub-mono truncate flex-1",style:{fontSize:12.5},children:e.jsx("span",{title:D.name,children:Ke(a,D.name)})}),e.jsxs("span",{className:"hub-mono hub-num flex-shrink-0",style:{fontSize:11,color:"var(--hub-ink-3)"},children:[R.visibleTools,"/",R.totalTools," tools"]})]},D.name)})}),e.jsxs("svg",{width:"80",height:"80",viewBox:"0 0 80 80",className:"self-center",children:[W.length===0?e.jsx("path",{d:"M0,40 C30,40 50,40 80,40",stroke:"var(--hub-line)",strokeWidth:"1",fill:"none",strokeDasharray:"3 3"}):W.map((D,R)=>{const r=12+60/Math.max(W.length,1)*(R+.5);return e.jsx("path",{d:`M0,${r} C 30,${r} 50,40 80,40`,stroke:"var(--hub-line)",strokeWidth:"1",fill:"none",strokeDasharray:"3 3"},R)}),e.jsx("circle",{cx:"80",cy:"40",r:"4",fill:"var(--hub-ink)"})]}),e.jsxs("div",{className:"px-3 py-2.5 rounded-md",style:{border:"1px solid var(--hub-line)",background:"var(--hub-bg-2)"},children:[e.jsx("div",{className:"hub-sect",style:{marginBottom:4},children:"endpoint"}),e.jsxs("div",{className:"hub-mono break-all",style:{fontSize:12,color:"var(--hub-ink-2)",lineHeight:1.4},children:[e.jsx("span",{style:{color:"var(--hub-ink-3)"},children:"/mcp/"}),e.jsx("b",{style:{color:"var(--hub-ink)",fontWeight:600},children:a.name})]}),e.jsxs("div",{className:"hub-mono break-all",style:{fontSize:12,color:"var(--hub-ink-2)",lineHeight:1.4},children:[e.jsx("span",{style:{color:"var(--hub-ink-3)"},children:"/api/"}),e.jsx("b",{style:{color:"var(--hub-ink)",fontWeight:600},children:a.name})]}),e.jsxs("div",{className:"flex gap-1.5 mt-2",children:[e.jsxs("button",{className:"hub-btn sm flex-1 justify-center",onClick:()=>C(z),title:i("common.copyUrl"),children:[e.jsx(Q,{size:11})," MCP"]}),e.jsxs("button",{className:"hub-btn sm flex-1 justify-center",onClick:()=>C(_),title:i("common.copyOpenApiUrl"),children:[e.jsx(Q,{size:11})," OpenAPI"]})]})]})]}),e.jsxs("div",{className:"flex justify-between items-center px-4 py-2",style:{borderTop:"1px solid var(--hub-line-2)",background:"var(--hub-bg-2)",fontSize:12,color:"var(--hub-ink-3)"},children:[e.jsxs("div",{children:[e.jsxs("div",{className:"hub-mono",children:[e.jsx("span",{style:{color:"var(--hub-ink-2)"},children:W.length})," ",i("nav.servers").toLowerCase()," ·"," ",e.jsx("span",{style:{color:"var(--hub-ink-2)"},children:J})," ",i("server.tools").toLowerCase()]}),h&&e.jsxs("div",{className:"hub-mono mt-1",style:{fontSize:11.5,color:"var(--hub-ink-3)"},children:[e.jsxs("div",{title:i("cost.estimate"),children:[i("cost.totalFootprint"),": ",H(h.direct.exposed),"/",H(h.direct.gross)]}),h.smartRouting&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{children:[i("cost.smartRouting"),": ",H(h.smartRouting.base)," (",i("cost.saved",{percent:Ne(h.direct.exposed,h.smartRouting.base)}),")"]}),e.jsxs("div",{title:i("cost.smartRoutingPdHint"),children:[i("cost.smartRoutingPd"),":"," ",H(h.smartRouting.progressiveDisclosure)]})]}),h.connectedCount<h.totalCount&&e.jsx("div",{children:i("cost.connectedOf",{connected:h.connectedCount,total:h.totalCount})})]})]}),E&&e.jsxs("button",{className:"hub-btn ghost sm",style:{color:"var(--hub-ink-3)"},onClick:()=>s(a),children:[i("groups.configureTools")||i("groups.edit"),e.jsx(Pe,{size:11,style:{transform:"rotate(-90deg)"}})]})]}),e.jsx(we,{isOpen:T!==null,target:T,onClose:()=>d(null)}),e.jsx($e,{isOpen:w,onClose:()=>j(!1),onConfirm:()=>{l(a.id),j(!1)},serverName:a.name,isGroup:!0})]})},Qe=({onSuccess:a,onCancel:m})=>{const{t:s}=G(),[l,h]=g.useState(""),[i,p]=g.useState(null),[E,I]=g.useState(!1),[N,A]=g.useState(null),x=`{
2
- "groups": [
3
- {
4
- "name": "AI Assistants",
5
- "servers": ["openai-server", "anthropic-server"]
6
- },
7
- {
8
- "name": "Development Tools",
9
- "servers": [
10
- {
11
- "name": "github-server",
12
- "tools": ["create_issue", "list_repos"],
13
- "prompts": ["triage_prompt"],
14
- "resources": ["resource://docs/repo-guide"]
15
- },
16
- {
17
- "name": "gitlab-server",
18
- "tools": "all",
19
- "prompts": "all",
20
- "resources": "all"
21
- }
22
- ]
23
- }
24
- ]
25
- }
26
-
27
- Supports:
28
- - Simple server list: ["server1", "server2"]
29
- - Advanced server config: [{"name": "server1", "tools": ["tool1"], "prompts": ["prompt1"], "resources": ["resource://docs/guide"]}]
30
- - All groups will be imported in a single efficient batch operation.`,w=t=>{try{const o=JSON.parse(t.trim());if(!o.groups||!Array.isArray(o.groups))return p(s("groupImport.invalidFormat")),null;for(const c of o.groups)if(!c.name||typeof c.name!="string")return p(s("groupImport.missingName")),null;return o}catch{return p(s("groupImport.parseError")),null}},j=()=>{p(null);const t=w(l);t&&A(t.groups)},T=async()=>{if(N){I(!0),p(null);try{const t=await se("/groups/batch",{groups:N});if(t.success){const{successCount:o,failureCount:c,results:C}=t;if(c>0){const z=C.filter(O=>!O.success).map(O=>`${O.name}: ${O.message||s("groupImport.addFailed")}`);p(s("groupImport.partialSuccess",{count:o,total:N.length})+`
31
- `+z.join(`
32
- `))}o>0&&a()}else p(t.message||s("groupImport.importFailed"))}catch(t){console.error("Import error:",t),p(s("groupImport.importFailed"))}finally{I(!1)}}},d=t=>e.jsx("span",{className:"text-gray-500 ml-2",children:s(`groups.${t}`)}),b=(t,o)=>{if(!o||o==="all")return null;const c=Array.isArray(o)?o.join(", "):o;return e.jsx("span",{className:"text-gray-500 ml-2",children:s(`groups.${t}`,{items:c})})},f=t=>!t||t.length===0?e.jsx("span",{className:"text-gray-500",children:s("groups.noServers")}):e.jsx("div",{className:"space-y-1",children:t.map((o,c)=>typeof o=="string"?e.jsxs("div",{className:"text-sm",children:["• ",o]},c):e.jsxs("div",{className:"text-sm",children:["• ",o.name,o.tools&&o.tools!=="all"&&e.jsxs("span",{className:"text-gray-500 ml-2",children:["(",Array.isArray(o.tools)?o.tools.join(", "):o.tools,")"]}),o.tools==="all"&&d("previewAllTools"),b("previewPrompts",o.prompts),o.prompts==="all"&&d("previewAllPrompts"),b("previewResources",o.resources),o.resources==="all"&&d("previewAllResources")]},c))});return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:s("groupImport.title")}),e.jsx("button",{onClick:m,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),i&&e.jsx("div",{className:"mb-4 bg-red-50 border-l-4 border-red-500 p-4 rounded",children:e.jsx("p",{className:"text-red-700 whitespace-pre-wrap",children:i})}),N?e.jsxs("div",{children:[e.jsxs("div",{className:"mb-4",children:[e.jsx("h3",{className:"text-lg font-medium text-gray-900 mb-3",children:s("groupImport.previewTitle")}),e.jsx("div",{className:"space-y-3",children:N.map((t,o)=>e.jsx("div",{className:"bg-gray-50 dark:bg-gray-800 p-4 rounded-lg border border-gray-200 dark:border-gray-700",children:e.jsx("div",{className:"flex items-start justify-between",children:e.jsxs("div",{className:"flex-1",children:[e.jsx("h4",{className:"font-medium text-gray-900",children:t.name}),t.description&&e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:t.description}),e.jsxs("div",{className:"mt-2 text-sm text-gray-600",children:[e.jsxs("strong",{children:[s("groups.servers"),":"]}),e.jsx("div",{className:"mt-1",children:f(t.servers)})]})]})})},o))})]}),e.jsxs("div",{className:"flex justify-end space-x-4",children:[e.jsx("button",{onClick:()=>A(null),disabled:E,className:"hub-btn",children:s("common.back")}),e.jsx("button",{onClick:T,disabled:E,className:"hub-btn primary",children:E?e.jsxs(e.Fragment,{children:[e.jsxs("svg",{className:"animate-spin h-4 w-4 mr-2",xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",children:[e.jsx("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),e.jsx("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"})]}),s("groupImport.importing")]}):s("groupImport.import")})]})]}):e.jsxs("div",{children:[e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:s("groupImport.inputLabel")}),e.jsx("textarea",{value:l,onChange:t=>h(t.target.value),className:"w-full h-96 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm",placeholder:x}),e.jsx("p",{className:"text-xs text-gray-500 mt-2",children:s("groupImport.inputHelp")})]}),e.jsxs("div",{className:"flex justify-end space-x-4",children:[e.jsx("button",{onClick:m,className:"hub-btn",children:s("common.cancel")}),e.jsx("button",{onClick:j,disabled:!l.trim(),className:"hub-btn primary",children:s("groupImport.preview")})]})]})]})})},Xe=({groups:a,onCancel:m})=>{const{t:s}=G(),[l,h]=g.useState(""),[i,p]=g.useState(""),[E,I]=g.useState([]),[N,A]=g.useState(!1),[x,w]=g.useState(!1),[j,T]=g.useState(null),d=t=>{I(o=>o.includes(t)?o.filter(c=>c!==t):[...o,t])},b=()=>{E.length===a.length?I([]):I(a.map(t=>t.id))},f=async()=>{if(!l.trim()){T(s("template.nameRequired"));return}w(!0),T(null);try{const t=await se("/templates/export",{name:l.trim(),description:i.trim()||void 0,groupIds:E.length>0?E:void 0,includeDisabledServers:N});if(t.success&&t.data){const o=t.data,c=new Blob([JSON.stringify(o,null,2)],{type:"application/json"}),C=URL.createObjectURL(c),z=document.createElement("a");z.href=C,z.download=`${o.name.replace(/[^a-zA-Z0-9-_]/g,"_")}.mcphub-template.json`,document.body.appendChild(z),z.click(),document.body.removeChild(z),URL.revokeObjectURL(C),m()}else T(t.message||s("template.exportFailed"))}catch(t){console.error("Export error:",t),T(s("template.exportFailed"))}finally{w(!1)}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-3xl max-h-[90vh] overflow-y-auto",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:s("template.exportTitle")}),e.jsx("button",{onClick:m,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),j&&e.jsx("div",{className:"mb-4 bg-red-50 dark:bg-red-900/20 border-l-4 border-red-500 p-4 rounded",children:e.jsx("p",{className:"text-red-700 dark:text-red-300",children:j})}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{children:[e.jsxs("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:[s("template.name")," *"]}),e.jsx("input",{type:"text",value:l,onChange:t=>h(t.target.value),className:"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",placeholder:s("template.namePlaceholder")})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-1",children:s("template.description")}),e.jsx("input",{type:"text",value:i,onChange:t=>p(t.target.value),className:"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",placeholder:s("template.descriptionPlaceholder")})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex justify-between items-center mb-2",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700",children:s("template.selectGroups")}),e.jsx("button",{onClick:b,className:"text-sm text-blue-600 hover:text-blue-800 dark:text-blue-300",children:E.length===a.length?s("template.deselectAll"):s("template.selectAll")})]}),e.jsx("p",{className:"text-xs text-gray-500 mb-2",children:s("template.selectGroupsHelp")}),e.jsx("div",{className:"border border-gray-200 dark:border-gray-700 rounded-md max-h-48 overflow-y-auto",children:a.map(t=>e.jsxs("label",{className:"flex items-center px-3 py-2 hover:bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:E.includes(t.id),onChange:()=>d(t.id),className:"mr-3 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:t.name}),t.description&&e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:t.description})]})]},t.id))})]}),e.jsxs("label",{className:"flex items-center space-x-2 cursor-pointer",children:[e.jsx("input",{type:"checkbox",checked:N,onChange:t=>A(t.target.checked),className:"h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"}),e.jsx("span",{className:"text-sm text-gray-700",children:s("template.includeDisabled")})]})]}),e.jsx("div",{className:"mt-4 p-3 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-700/60 rounded-md",children:e.jsx("p",{className:"text-sm text-blue-700 dark:text-blue-300",children:s("template.exportNote")})}),e.jsxs("div",{className:"flex justify-end space-x-4 mt-6",children:[e.jsx("button",{onClick:m,className:"hub-btn",children:s("common.cancel")}),e.jsx("button",{onClick:f,disabled:x||!l.trim(),className:"hub-btn primary",children:s(x?"template.exporting":"template.export")})]})]})})},Ye=({onSuccess:a,onCancel:m})=>{const{t:s}=G(),[l,h]=g.useState(null),[i,p]=g.useState(null),[E,I]=g.useState(!1),[N,A]=g.useState(null),x=g.useRef(null),w=d=>{var t;p(null),h(null),A(null);const b=(t=d.target.files)==null?void 0:t[0];if(!b)return;const f=new FileReader;f.onload=o=>{var c;try{const C=JSON.parse((c=o.target)==null?void 0:c.result);if(!C.version||!C.name||!C.servers||!C.groups){p(s("template.invalidFormat"));return}h(C)}catch{p(s("template.parseError"))}},f.readAsText(b)},j=d=>{if(p(null),h(null),A(null),!!d.trim())try{const b=JSON.parse(d.trim());if(!b.version||!b.name||!b.servers||!b.groups){p(s("template.invalidFormat"));return}h(b)}catch{p(s("template.parseError"))}},T=async()=>{if(l){I(!0),p(null);try{const d=await se("/templates/import",l);d.data?(A(d.data),d.data.success&&a()):p(d.message||s("template.importFailed"))}catch(d){console.error("Import error:",d),p(s("template.importFailed"))}finally{I(!1)}}};return e.jsx("div",{className:"fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4",children:e.jsxs("div",{className:"bg-white dark:bg-gray-800 shadow rounded-lg p-6 w-full max-w-4xl max-h-[90vh] overflow-y-auto",children:[e.jsxs("div",{className:"flex justify-between items-center mb-6",children:[e.jsx("h2",{className:"text-xl font-semibold text-gray-900",children:s("template.importTitle")}),e.jsx("button",{onClick:m,className:"text-gray-500 hover:text-gray-700",children:"✕"})]}),i&&e.jsx("div",{className:"mb-4 bg-red-50 dark:bg-red-900/20 border-l-4 border-red-500 p-4 rounded",children:e.jsx("p",{className:"text-red-700 dark:text-red-300",children:i})}),N&&e.jsxs("div",{className:`mb-4 p-4 rounded border-l-4 ${N.success?"bg-green-50 dark:bg-green-900/20 border-green-500":"bg-yellow-50 dark:bg-yellow-900/20 border-yellow-500"}`,children:[e.jsx("p",{className:N.success?"text-green-700 dark:text-green-300":"text-yellow-700 dark:text-yellow-300",children:s("template.importResult",{serversCreated:N.serversCreated,serversSkipped:N.serversSkipped,groupsCreated:N.groupsCreated,groupsSkipped:N.groupsSkipped})}),N.requiredEnvVars.length>0&&e.jsxs("div",{className:"mt-2 p-2 bg-orange-50 dark:bg-orange-900/20 border border-orange-200 dark:border-orange-700/60 rounded",children:[e.jsx("p",{className:"text-sm font-medium text-orange-800 dark:text-orange-300",children:s("template.envVarsNeeded")}),e.jsx("ul",{className:"mt-1 text-sm text-orange-700 dark:text-orange-300",children:N.requiredEnvVars.map(d=>e.jsx("li",{className:"font-mono",children:d},d))})]})]}),l?e.jsxs("div",{children:[e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"p-3 bg-gray-50 dark:bg-gray-800 rounded-md",children:[e.jsx("h3",{className:"font-medium text-gray-900",children:l.name}),l.description&&e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:l.description}),e.jsxs("p",{className:"text-xs text-gray-500 mt-1",children:[s("template.version"),": ",l.version," | ",s("template.createdAt"),":"," ",new Date(l.createdAt).toLocaleDateString()]})]}),e.jsxs("div",{children:[e.jsxs("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:[s("template.servers")," (",Object.keys(l.servers).length,")"]}),e.jsx("div",{className:"border border-gray-200 dark:border-gray-700 rounded-md divide-y divide-gray-200 dark:divide-gray-700 max-h-40 overflow-y-auto",children:Object.entries(l.servers).map(([d,b])=>e.jsxs("div",{className:"px-3 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:d}),e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:b.type||"stdio"})]},d))})]}),e.jsxs("div",{children:[e.jsxs("h4",{className:"text-sm font-medium text-gray-700 mb-2",children:[s("template.groups")," (",l.groups.length,")"]}),e.jsx("div",{className:"border border-gray-200 dark:border-gray-700 rounded-md divide-y divide-gray-200 dark:divide-gray-700 max-h-40 overflow-y-auto",children:l.groups.map((d,b)=>e.jsxs("div",{className:"px-3 py-2",children:[e.jsx("span",{className:"text-sm font-medium text-gray-900",children:d.name}),d.description&&e.jsx("span",{className:"text-xs text-gray-500 ml-2",children:d.description}),e.jsxs("div",{className:"text-xs text-gray-500 mt-1",children:[d.servers.length," ",s("template.serversInGroup")]})]},b))})]}),l.requiredEnvVars.length>0&&e.jsxs("div",{className:"p-3 bg-orange-50 dark:bg-orange-900/20 border border-orange-200 dark:border-orange-700/60 rounded-md",children:[e.jsx("h4",{className:"text-sm font-medium text-orange-800 dark:text-orange-300",children:s("template.envVarsNeeded")}),e.jsx("ul",{className:"mt-1 text-sm text-orange-700 dark:text-orange-300 font-mono",children:l.requiredEnvVars.map(d=>e.jsx("li",{children:d},d))})]})]}),e.jsxs("div",{className:"flex justify-end space-x-4 mt-6",children:[e.jsx("button",{onClick:()=>{h(null),A(null),x.current&&(x.current.value="")},className:"hub-btn",children:s("common.back")}),e.jsx("button",{onClick:T,disabled:E,className:"hub-btn primary",children:s(E?"template.importing":"template.import")})]})]}):e.jsxs("div",{children:[e.jsxs("div",{className:"mb-4",children:[e.jsx("div",{className:"flex items-center justify-between mb-2",children:e.jsx("label",{className:"block text-sm font-medium text-gray-700",children:s("template.uploadFile")})}),e.jsx("div",{className:"flex items-center space-x-4",children:e.jsx("input",{ref:x,type:"file",accept:".json",onChange:w,className:"block text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"})})]}),e.jsxs("div",{className:"relative my-4",children:[e.jsx("div",{className:"absolute inset-0 flex items-center",children:e.jsx("div",{className:"w-full border-t border-gray-300"})}),e.jsx("div",{className:"relative flex justify-center text-sm",children:e.jsx("span",{className:"bg-white dark:bg-gray-800 px-2 text-gray-500",children:s("template.or")})})]}),e.jsxs("div",{className:"mb-4",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:s("template.pasteJson")}),e.jsx("textarea",{className:"w-full h-64 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm",placeholder:s("template.pastePlaceholder"),onChange:d=>j(d.target.value)})]}),e.jsx("div",{className:"flex justify-end space-x-4",children:e.jsx("button",{onClick:m,className:"hub-btn",children:s("common.cancel")})})]})]})})},os=()=>{const{t:a}=G(),{groups:m,loading:s,error:l,setError:h,deleteGroup:i,triggerRefresh:p}=ee(),{allServers:E}=te({refreshOnMount:!0}),{groupCosts:I,refetch:N}=re();g.useEffect(()=>{N()},[m,E,N]);const[A,x]=g.useState(null),[w,j]=g.useState(!1),[T,d]=g.useState(!1),[b,f]=g.useState(!1),[t,o]=g.useState(!1),c=async C=>{const z=await i(C);(!z||!z.success)&&h((z==null?void 0:z.message)||a("groups.deleteError"))};return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-end justify-between gap-4 mb-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"hub-h1",children:a("pages.groups.title")}),e.jsxs("p",{className:"hub-sub",children:[e.jsx("span",{className:"hub-num",children:m.length})," ",a("nav.groups").toLowerCase()]})]}),e.jsxs("div",{className:"flex gap-2",children:[e.jsxs("button",{className:"hub-btn",onClick:()=>d(!0),children:[e.jsx(ce,{size:13})," ",a("groupImport.button")]}),e.jsxs("button",{className:"hub-btn",onClick:()=>f(!0),children:[e.jsx(Re,{size:13})," ",a("template.exportButton")]}),e.jsxs("button",{className:"hub-btn",onClick:()=>o(!0),children:[e.jsx(ce,{size:13})," ",a("template.importButton")]}),e.jsxs("button",{className:"hub-btn primary",onClick:()=>j(!0),children:[e.jsx(de,{size:13})," ",a("groups.add")]})]})]}),l&&e.jsxs("div",{className:"hub-card flex items-center justify-between gap-3 mb-4",style:{padding:"10px 14px",borderColor:"oklch(0.85 0.1 25)",background:"oklch(0.97 0.03 25)",color:"oklch(0.4 0.18 25)"},children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx(Le,{size:14,className:"flex-shrink-0"}),e.jsx("span",{className:"truncate text-[13px]",children:l})]}),e.jsx("button",{className:"hub-icon-btn sm",onClick:()=>h(null),children:e.jsx(Ue,{size:13})})]}),s?e.jsx("div",{className:"hub-card p-6 text-center",style:{color:"var(--hub-ink-3)"},children:a("app.loading")}):m.length===0?e.jsx("div",{className:"hub-card p-10 text-center",style:{color:"var(--hub-ink-3)"},children:a("groups.noGroups")}):e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-3.5",children:[m.map(C=>e.jsx(Je,{group:C,servers:E,onEdit:x,onDelete:c,cost:I.find(z=>z.id===C.id)},C.id)),e.jsx("button",{onClick:()=>j(!0),className:"flex items-center justify-center text-[var(--hub-ink-3)] hover:text-[var(--hub-ink-2)] transition-colors",style:{border:"1px dashed var(--hub-line)",borderRadius:10,minHeight:200,background:"transparent",cursor:"pointer"},children:e.jsxs("div",{className:"text-center",children:[e.jsx("div",{className:"grid place-items-center mx-auto mb-2",style:{width:36,height:36,borderRadius:10,border:"1px solid var(--hub-line)"},children:e.jsx(de,{size:16})}),e.jsx("div",{style:{fontSize:13,fontWeight:500,color:"var(--hub-ink-2)"},children:a("groups.add")}),e.jsx("div",{style:{fontSize:12,marginTop:2},children:a("groups.addNew")})]})})]}),w&&e.jsx(Be,{onAdd:()=>{j(!1),p()},onCancel:()=>j(!1)}),T&&e.jsx(Qe,{onSuccess:()=>{d(!1),p()},onCancel:()=>d(!1)}),A&&e.jsx(Ve,{group:A,onEdit:()=>{x(null),p()},onCancel:()=>x(null)}),b&&e.jsx(Xe,{groups:m,onCancel:()=>f(!1)}),t&&e.jsx(Ye,{onSuccess:()=>{o(!1),p()},onCancel:()=>o(!1)})]})};export{os as default};
33
- //# sourceMappingURL=GroupsPage-C9uf7qRF.js.map