@undefineds.co/xpod 0.4.11 → 0.4.12

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 (372) hide show
  1. package/config/components-ignore.json +5 -1
  2. package/config/notifications.json +68 -0
  3. package/config/xpod.base.json +9 -11
  4. package/dist/ai/service/AiCredentialSecret.d.ts +30 -0
  5. package/dist/ai/service/AiCredentialSecret.js +120 -0
  6. package/dist/ai/service/CredentialReaderImpl.d.ts +18 -0
  7. package/dist/ai/service/CredentialReaderImpl.js +81 -5
  8. package/dist/ai/service/CredentialReaderImpl.jsonld +42 -2
  9. package/dist/ai/service/EmbeddingModelPolicy.d.ts +79 -0
  10. package/dist/ai/service/EmbeddingModelPolicy.js +134 -0
  11. package/dist/ai/service/EmbeddingProviderError.js +6 -0
  12. package/dist/ai/service/EmbeddingServiceImpl.d.ts +13 -1
  13. package/dist/ai/service/EmbeddingServiceImpl.js +18 -5
  14. package/dist/ai/service/EmbeddingServiceImpl.jsonld +35 -0
  15. package/dist/ai/service/index.d.ts +2 -0
  16. package/dist/ai/service/index.js +2 -0
  17. package/dist/api/ai-gateway/connect/index.d.ts +20 -2
  18. package/dist/api/ai-gateway/connect/index.js +162 -16
  19. package/dist/api/ai-gateway/credentials/AiCredentialSecretDecoder.d.ts +12 -0
  20. package/dist/api/ai-gateway/credentials/AiCredentialSecretDecoder.js +30 -0
  21. package/dist/api/ai-gateway/errors.d.ts +1 -1
  22. package/dist/api/ai-gateway/models/GatewayEmbeddingModelCatalog.d.ts +11 -0
  23. package/dist/api/ai-gateway/models/GatewayEmbeddingModelCatalog.js +31 -0
  24. package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +7 -3
  25. package/dist/api/ai-gateway/models/ProviderCustomModelsService.d.ts +18 -0
  26. package/dist/api/ai-gateway/models/ProviderCustomModelsService.js +23 -0
  27. package/dist/api/ai-gateway/models/ProviderModelDiscoveryAdapters.d.ts +3 -1
  28. package/dist/api/ai-gateway/models/ProviderModelDiscoveryAdapters.js +2 -45
  29. package/dist/api/ai-gateway/models/ProviderModelSelectionService.d.ts +10 -0
  30. package/dist/api/ai-gateway/models/ProviderModelSelectionService.js +57 -20
  31. package/dist/api/ai-gateway/models/ProviderModelType.d.ts +28 -0
  32. package/dist/api/ai-gateway/models/ProviderModelType.js +34 -0
  33. package/dist/api/ai-gateway/models/ProviderModelsAdapter.d.ts +10 -0
  34. package/dist/api/ai-gateway/models/ProviderModelsAdapter.js +3 -4
  35. package/dist/api/ai-gateway/models/index.d.ts +1 -0
  36. package/dist/api/ai-gateway/models/index.js +3 -1
  37. package/dist/api/ai-gateway/providers/CustomRuntimeAdapter.d.ts +7 -0
  38. package/dist/api/ai-gateway/providers/CustomRuntimeAdapter.js +9 -2
  39. package/dist/api/ai-gateway/providers/ModelsDevCatalog.d.ts +7 -1
  40. package/dist/api/ai-gateway/providers/ModelsDevCatalog.js +4 -10
  41. package/dist/api/ai-gateway/providers/ProviderRegistry.d.ts +107 -0
  42. package/dist/api/ai-gateway/providers/ProviderRegistry.js +244 -21
  43. package/dist/api/ai-gateway/providers/ProviderRuntimeRegistry.d.ts +5 -0
  44. package/dist/api/ai-gateway/providers/ProviderRuntimeRegistry.js +5 -1
  45. package/dist/api/ai-gateway/quota/SubscriptionQuotaAdapters.d.ts +12 -4
  46. package/dist/api/ai-gateway/quota/SubscriptionQuotaAdapters.js +11 -11
  47. package/dist/api/ai-gateway/routing/ModelRouter.d.ts +14 -0
  48. package/dist/api/ai-gateway/routing/ModelRouter.js +43 -4
  49. package/dist/api/ai-gateway/service-access/AiConnectionsServiceAccess.d.ts +9 -1
  50. package/dist/api/ai-gateway/service-access/AiConnectionsServiceAccess.js +3 -27
  51. package/dist/api/chatkit/pod-store.d.ts +31 -0
  52. package/dist/api/chatkit/pod-store.js +132 -8
  53. package/dist/api/container/common.js +24 -4
  54. package/dist/api/container/routes.js +17 -2
  55. package/dist/api/container/types.d.ts +3 -1
  56. package/dist/api/handlers/AdminHandler.d.ts +13 -0
  57. package/dist/api/handlers/AdminHandler.js +47 -18
  58. package/dist/api/handlers/AiConfigHandler.d.ts +6 -0
  59. package/dist/api/handlers/AiConfigHandler.js +28 -0
  60. package/dist/api/handlers/AiGatewayManagementHandler.js +36 -0
  61. package/dist/api/runs/RdfRunContextRetriever.d.ts +14 -0
  62. package/dist/api/runs/RdfRunContextRetriever.js +41 -5
  63. package/dist/api/runtime.js +2 -1
  64. package/dist/cli/commands/secret.d.ts +1 -0
  65. package/dist/cli/commands/secret.js +25 -7
  66. package/dist/components/components.jsonld +3 -1
  67. package/dist/components/context.jsonld +53 -1
  68. package/dist/http/SubgraphSparqlHttpHandler.d.ts +59 -2
  69. package/dist/http/SubgraphSparqlHttpHandler.js +145 -13
  70. package/dist/http/SubgraphSparqlHttpHandler.jsonld +37 -0
  71. package/dist/identity/drizzle/EdgeNodeRepository.d.ts +0 -12
  72. package/dist/identity/drizzle/EdgeNodeRepository.js +5 -13
  73. package/dist/index.d.ts +2 -1
  74. package/dist/index.js +7 -3
  75. package/dist/logging/log-file.d.ts +17 -0
  76. package/dist/logging/log-file.js +69 -0
  77. package/dist/main.js +2 -1
  78. package/dist/notifications/NotificationChannelSweeper.d.ts +50 -0
  79. package/dist/notifications/NotificationChannelSweeper.js +131 -0
  80. package/dist/notifications/NotificationChannelSweeper.jsonld +136 -0
  81. package/dist/notifications/ReclaimingWebSocket2023Storer.d.ts +25 -0
  82. package/dist/notifications/ReclaimingWebSocket2023Storer.js +56 -0
  83. package/dist/notifications/ReclaimingWebSocket2023Storer.jsonld +77 -0
  84. package/dist/notifications/index.d.ts +2 -0
  85. package/dist/notifications/index.js +2 -0
  86. package/dist/runtime/Proxy.d.ts +37 -0
  87. package/dist/runtime/Proxy.js +88 -16
  88. package/dist/runtime/bootstrap.js +2 -1
  89. package/dist/runtime/upgrade/BunNativeUpgradeRelay.d.ts +116 -0
  90. package/dist/runtime/upgrade/BunNativeUpgradeRelay.js +423 -0
  91. package/dist/runtime/upgrade/UpgradeHandshake.d.ts +63 -0
  92. package/dist/runtime/upgrade/UpgradeHandshake.js +199 -0
  93. package/dist/runtime/upgrade/WebSocketFrames.d.ts +67 -0
  94. package/dist/runtime/upgrade/WebSocketFrames.js +224 -0
  95. package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.js +8 -1
  96. package/node_modules/@undefineds.co/ai-connections/dist/AiAuthorizationActions.d.ts +11 -2
  97. package/node_modules/@undefineds.co/ai-connections/dist/AiAuthorizationActions.js +27 -10
  98. package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.d.ts +7 -0
  99. package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.js +13 -11
  100. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectDialog.js +1 -1
  101. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsList.js +10 -6
  102. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.d.ts +15 -0
  103. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +29 -3
  104. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMainHeader.js +18 -3
  105. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.d.ts +16 -1
  106. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.js +92 -20
  107. package/node_modules/@undefineds.co/ai-connections/dist/AiCopyButton.d.ts +35 -0
  108. package/node_modules/@undefineds.co/ai-connections/dist/AiCopyButton.js +45 -0
  109. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +71 -11
  110. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialRow.js +9 -7
  111. package/node_modules/@undefineds.co/ai-connections/dist/AiEndpointList.d.ts +23 -0
  112. package/node_modules/@undefineds.co/ai-connections/dist/AiEndpointList.js +24 -0
  113. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.d.ts +13 -4
  114. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +35 -38
  115. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayModelsSection.d.ts +21 -0
  116. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayModelsSection.js +51 -0
  117. package/node_modules/@undefineds.co/ai-connections/dist/AiInfoTooltip.d.ts +15 -0
  118. package/node_modules/@undefineds.co/ai-connections/dist/AiInfoTooltip.js +16 -0
  119. package/node_modules/@undefineds.co/ai-connections/dist/AiModelCatalog.d.ts +175 -0
  120. package/node_modules/@undefineds.co/ai-connections/dist/AiModelCatalog.js +133 -0
  121. package/node_modules/@undefineds.co/ai-connections/dist/AiOfferingDetails.js +2 -2
  122. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.js +13 -64
  123. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderHeader.d.ts +21 -0
  124. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderHeader.js +12 -0
  125. package/node_modules/@undefineds.co/ai-connections/dist/AiRowAction.d.ts +15 -0
  126. package/node_modules/@undefineds.co/ai-connections/dist/AiRowAction.js +12 -0
  127. package/node_modules/@undefineds.co/ai-connections/dist/AiSortableCredentialList.js +2 -1
  128. package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.d.ts +7 -0
  129. package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.js +6 -0
  130. package/node_modules/@undefineds.co/ai-connections/dist/authorization-methods.d.ts +36 -5
  131. package/node_modules/@undefineds.co/ai-connections/dist/authorization-methods.js +52 -9
  132. package/node_modules/@undefineds.co/ai-connections/dist/client/normalize.js +30 -3
  133. package/node_modules/@undefineds.co/ai-connections/dist/client/types.d.ts +21 -1
  134. package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.d.ts +1 -3
  135. package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.js +3 -7
  136. package/node_modules/@undefineds.co/ai-connections/dist/client-config/index.cjs +9 -7
  137. package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.d.ts +46 -0
  138. package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.js +231 -0
  139. package/node_modules/@undefineds.co/ai-connections/dist/collections.d.ts +119 -0
  140. package/node_modules/@undefineds.co/ai-connections/dist/collections.js +173 -0
  141. package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +71 -5
  142. package/node_modules/@undefineds.co/ai-connections/dist/controller.js +285 -16
  143. package/node_modules/@undefineds.co/ai-connections/dist/credential-labels.d.ts +5 -0
  144. package/node_modules/@undefineds.co/ai-connections/dist/credential-labels.js +8 -3
  145. package/node_modules/@undefineds.co/ai-connections/dist/credential-storage.d.ts +59 -0
  146. package/node_modules/@undefineds.co/ai-connections/dist/credential-storage.js +131 -0
  147. package/node_modules/@undefineds.co/ai-connections/dist/endpoint-urls.d.ts +15 -0
  148. package/node_modules/@undefineds.co/ai-connections/dist/endpoint-urls.js +20 -0
  149. package/node_modules/@undefineds.co/ai-connections/dist/gateway-endpoints.d.ts +14 -0
  150. package/node_modules/@undefineds.co/ai-connections/dist/gateway-endpoints.js +20 -0
  151. package/node_modules/@undefineds.co/ai-connections/dist/index.d.ts +8 -0
  152. package/node_modules/@undefineds.co/ai-connections/dist/index.js +8 -0
  153. package/node_modules/@undefineds.co/ai-connections/dist/manifest.js +1 -1
  154. package/node_modules/@undefineds.co/ai-connections/dist/offering-endpoints.d.ts +14 -0
  155. package/node_modules/@undefineds.co/ai-connections/dist/offering-endpoints.js +17 -0
  156. package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.cjs +68 -1
  157. package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.d.ts +75 -1
  158. package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.js +119 -0
  159. package/node_modules/@undefineds.co/ai-connections/dist/provider-visuals.d.ts +5 -0
  160. package/node_modules/@undefineds.co/ai-connections/dist/provider-visuals.js +5 -0
  161. package/node_modules/@undefineds.co/ai-connections/dist/service-access.js +2 -27
  162. package/node_modules/@undefineds.co/ai-connections/package.json +1 -0
  163. package/node_modules/@undefineds.co/extension-sdk/dist/manifest.d.ts +1 -1
  164. package/node_modules/@undefineds.co/extension-sdk/dist/manifest.js +2 -0
  165. package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +150 -0
  166. package/node_modules/@undefineds.co/extension-sdk/package.json +2 -1
  167. package/node_modules/@undefineds.co/models/README.md +1 -1
  168. package/node_modules/@undefineds.co/models/dist/ai-config/index.d.ts +3 -3
  169. package/node_modules/@undefineds.co/models/dist/ai-gateway.schema.d.ts +5 -0
  170. package/node_modules/@undefineds.co/models/dist/ai-gateway.schema.js +9 -0
  171. package/node_modules/@undefineds.co/models/dist/ai-runtime.repository.d.ts +1 -1
  172. package/node_modules/@undefineds.co/models/dist/approval.repository.d.ts +2 -2
  173. package/node_modules/@undefineds.co/models/dist/audit.presentation.d.ts +2 -2
  174. package/node_modules/@undefineds.co/models/dist/capture.repository.d.ts +1 -1
  175. package/node_modules/@undefineds.co/models/dist/chat-project.repository.d.ts +1 -1
  176. package/node_modules/@undefineds.co/models/dist/chat.repository.d.ts +2 -2
  177. package/node_modules/@undefineds.co/models/dist/control-request.repository.d.ts +2 -2
  178. package/node_modules/@undefineds.co/models/dist/conversation-share.repository.d.ts +1 -1
  179. package/node_modules/@undefineds.co/models/dist/credential.schema.d.ts +12 -0
  180. package/node_modules/@undefineds.co/models/dist/credential.schema.js +15 -1
  181. package/node_modules/@undefineds.co/models/dist/discovery/index.d.ts +2 -2
  182. package/node_modules/@undefineds.co/models/dist/discovery/providers.json +248 -7
  183. package/node_modules/@undefineds.co/models/dist/discovery/types.d.ts +31 -0
  184. package/node_modules/@undefineds.co/models/dist/extension/index.d.ts +1 -1
  185. package/node_modules/@undefineds.co/models/dist/favorite/index.d.ts +2 -2
  186. package/node_modules/@undefineds.co/models/dist/fixtures/contracts-chat-contact.d.ts +5 -5
  187. package/node_modules/@undefineds.co/models/dist/import/index.d.ts +1 -1
  188. package/node_modules/@undefineds.co/models/dist/index.d.ts +73 -73
  189. package/node_modules/@undefineds.co/models/dist/index.js +1 -1
  190. package/node_modules/@undefineds.co/models/dist/input-request.repository.d.ts +2 -2
  191. package/node_modules/@undefineds.co/models/dist/knowledge/index.d.ts +1 -1
  192. package/node_modules/@undefineds.co/models/dist/message.repository.d.ts +1 -1
  193. package/node_modules/@undefineds.co/models/dist/namespaces.js +5 -0
  194. package/node_modules/@undefineds.co/models/dist/pod-storage-descriptor.d.ts +4815 -26
  195. package/node_modules/@undefineds.co/models/dist/pod-storage-descriptor.js +482 -132
  196. package/node_modules/@undefineds.co/models/dist/schema.d.ts +4 -0
  197. package/node_modules/@undefineds.co/models/dist/session/index.d.ts +1 -1
  198. package/node_modules/@undefineds.co/models/dist/settings/index.d.ts +1 -1
  199. package/node_modules/@undefineds.co/models/dist/sidecar/persistence-mapping.d.ts +5 -5
  200. package/node_modules/@undefineds.co/models/dist/thread.repository.d.ts +2 -2
  201. package/node_modules/@undefineds.co/models/dist/types/message-block.d.ts +1 -1
  202. package/node_modules/@undefineds.co/models/dist/vocab/_namespaces.d.ts +1 -1
  203. package/node_modules/@undefineds.co/models/dist/vocab/index.d.ts +10 -10
  204. package/node_modules/@undefineds.co/models/dist/vocab/sidecar.vocab.d.ts +1 -0
  205. package/node_modules/@undefineds.co/models/dist/vocab/sidecar.vocab.js +1 -0
  206. package/node_modules/@undefineds.co/models/package.json +4 -2
  207. package/node_modules/@undefineds.co/pod-collections/README.md +71 -0
  208. package/node_modules/@undefineds.co/pod-collections/dist/diff.d.ts +85 -0
  209. package/node_modules/@undefineds.co/pod-collections/dist/diff.js +166 -0
  210. package/node_modules/@undefineds.co/pod-collections/dist/feed.d.ts +17 -0
  211. package/node_modules/@undefineds.co/pod-collections/dist/feed.js +27 -0
  212. package/node_modules/@undefineds.co/pod-collections/dist/index.cjs +1042 -0
  213. package/node_modules/@undefineds.co/pod-collections/dist/index.d.ts +37 -0
  214. package/node_modules/@undefineds.co/pod-collections/dist/index.js +89 -0
  215. package/node_modules/@undefineds.co/pod-collections/dist/layout.d.ts +75 -0
  216. package/node_modules/@undefineds.co/pod-collections/dist/layout.js +173 -0
  217. package/node_modules/@undefineds.co/pod-collections/dist/mapping.d.ts +78 -0
  218. package/node_modules/@undefineds.co/pod-collections/dist/mapping.js +287 -0
  219. package/node_modules/@undefineds.co/pod-collections/dist/mutations.d.ts +83 -0
  220. package/node_modules/@undefineds.co/pod-collections/dist/mutations.js +273 -0
  221. package/node_modules/@undefineds.co/pod-collections/dist/react.d.ts +21 -0
  222. package/node_modules/@undefineds.co/pod-collections/dist/react.js +22 -0
  223. package/node_modules/@undefineds.co/pod-collections/dist/read.d.ts +65 -0
  224. package/node_modules/@undefineds.co/pod-collections/dist/read.js +31 -0
  225. package/node_modules/@undefineds.co/pod-collections/dist/sync.d.ts +66 -0
  226. package/node_modules/@undefineds.co/pod-collections/dist/sync.js +260 -0
  227. package/node_modules/@undefineds.co/pod-collections/dist/types.d.ts +155 -0
  228. package/node_modules/@undefineds.co/pod-collections/dist/types.js +9 -0
  229. package/node_modules/@undefineds.co/pod-collections/package.json +42 -0
  230. package/package.json +15 -7
  231. package/static/app/assets/main.css +1 -1
  232. package/static/auth-callback/assets/AiConfigContext-DnQs1ZpW.js +1 -0
  233. package/static/auth-callback/assets/AiConfigPage-DOtvlxH0.js +1 -0
  234. package/static/auth-callback/assets/DocumentProcessingPanel-BS2voQgL.js +1 -0
  235. package/static/auth-callback/assets/IndexLifecyclePanel-Cvt0cJtN.js +1 -0
  236. package/static/auth-callback/assets/{IndexSubjectPanel-BbUqop4b.js → IndexSubjectPanel-Dhf01GqF.js} +1 -1
  237. package/static/auth-callback/assets/ModelAssignmentsPanel-BJutWaAF.js +1 -0
  238. package/static/auth-callback/assets/ModelsPage-BRS0sje3.js +11 -0
  239. package/static/auth-callback/assets/{NetworkPage-Vp2PrMxs.js → NetworkPage-B3qBfANA.js} +2 -2
  240. package/static/auth-callback/assets/{PaneListHeader-DY7baSFl.js → PaneListHeader-QQeJGTp4.js} +1 -1
  241. package/static/auth-callback/assets/SearchIndexingPanel-MyNUSEwT.js +1 -0
  242. package/static/auth-callback/assets/{StatusSubjectPanel-ArIS-TxT.js → StatusSubjectPanel-DQkEVsFs.js} +1 -1
  243. package/static/{settings/assets/StatusWorkspace-CoMFRXoV.js → auth-callback/assets/StatusWorkspace-Dkv63Uwk.js} +1 -1
  244. package/static/auth-callback/assets/{SystemSettingsPage-BDvT1NUz.js → SystemSettingsPage-TIsUfX5d.js} +1 -1
  245. package/static/{dashboard/assets/SystemSettingsSubjectPanel-WaE8mTQi.js → auth-callback/assets/SystemSettingsSubjectPanel-BJWEt2-7.js} +5 -5
  246. package/static/auth-callback/assets/XpodAiConnectionsPodStore-BJiXm-PF.js +3 -0
  247. package/static/auth-callback/assets/{activity-D1Dsfa_0.js → activity-5hNeNKnL.js} +1 -1
  248. package/static/auth-callback/assets/ai-connections-CryuHGKW.js +1 -0
  249. package/static/auth-callback/assets/{auth-callback-Cmf4hXVZ.js → auth-callback-CAFZ3R0B.js} +6 -6
  250. package/static/auth-callback/assets/auth-callback-rk080mvG.css +1 -0
  251. package/static/auth-callback/assets/{badge-CZsHq3Ih.js → badge-CAIPI0CB.js} +1 -1
  252. package/static/auth-callback/assets/collection-runtime-D9s7h9YA.js +1 -0
  253. package/static/auth-callback/assets/dialog-BjA109aX.js +1 -0
  254. package/static/auth-callback/assets/{download-DU9ntlHw.js → download-B3Up5W2G.js} +1 -1
  255. package/static/auth-callback/assets/{external-link-BfzaaZDx.js → external-link-BTXgSPzB.js} +1 -1
  256. package/static/auth-callback/assets/index-BppXlxcp.js +41 -0
  257. package/static/auth-callback/assets/index-CirW4CJf.js +1 -0
  258. package/static/auth-callback/assets/index-DlLYWOhw.js +4 -0
  259. package/static/auth-callback/assets/{index-browser-BNFqqC74.js → index-browser-DtU38ni3.js} +1 -1
  260. package/static/auth-callback/assets/{key-round-BwP3DBWo.js → key-round-CQCOrZ7x.js} +1 -1
  261. package/static/auth-callback/assets/layout-DB3pMyTl.js +1 -0
  262. package/static/auth-callback/assets/pod-collections-host-KnnGFZHL.js +8 -0
  263. package/static/auth-callback/assets/{rotate-ccw-VMwnmyab.js → rotate-ccw-C1zM2KtS.js} +1 -1
  264. package/static/auth-callback/assets/{skeleton-CoD55rBT.js → skeleton-Cqp7_M9-.js} +1 -1
  265. package/static/auth-callback/assets/{waypoints-DRo_1Ca7.js → waypoints-Bj6gAUSQ.js} +1 -1
  266. package/static/auth-callback/assets/{workspace-layout--mcXl3My.js → workspace-layout-DK8mDzrY.js} +1 -1
  267. package/static/auth-callback/auth-callback.html +2 -2
  268. package/static/dashboard/assets/AiConfigContext-BsE7MqX8.js +1 -0
  269. package/static/dashboard/assets/AiConfigPage-DvM-lRXa.js +1 -0
  270. package/static/dashboard/assets/DocumentProcessingPanel-BPfdlfuf.js +1 -0
  271. package/static/dashboard/assets/IndexLifecyclePanel--m0AGxvh.js +1 -0
  272. package/static/dashboard/assets/{IndexSubjectPanel-B2cXwl8X.js → IndexSubjectPanel-Bb7m1k3d.js} +1 -1
  273. package/static/dashboard/assets/ModelAssignmentsPanel-BLi_zbMa.js +1 -0
  274. package/static/dashboard/assets/ModelsPage-CxdpgnfR.js +11 -0
  275. package/static/dashboard/assets/{NetworkPage-DV2V0qWO.js → NetworkPage-BAIPw-D_.js} +2 -2
  276. package/static/dashboard/assets/{PaneListHeader-D0c1yCFH.js → PaneListHeader-BBCMei0r.js} +1 -1
  277. package/static/dashboard/assets/SearchIndexingPanel-Ck4Y-xjR.js +1 -0
  278. package/static/dashboard/assets/{StatusSubjectPanel-oPSECj7i.js → StatusSubjectPanel-D1KWfAXE.js} +1 -1
  279. package/static/dashboard/assets/{StatusWorkspace-PdovQPKa.js → StatusWorkspace-JbA5gzXx.js} +1 -1
  280. package/static/dashboard/assets/{SystemSettingsPage-CtNDklkx.js → SystemSettingsPage-BbwQWsSR.js} +1 -1
  281. package/static/{settings/assets/SystemSettingsSubjectPanel-DExjDo_v.js → dashboard/assets/SystemSettingsSubjectPanel-N117nMt5.js} +5 -5
  282. package/static/dashboard/assets/XpodAiConnectionsPodStore-CdDOb4SV.js +18 -0
  283. package/static/dashboard/assets/{activity-B9ylSoyr.js → activity-DX1I96o_.js} +1 -1
  284. package/static/dashboard/assets/ai-connections-DwCRqOQa.js +1 -0
  285. package/static/dashboard/assets/{badge-BNbFbpIb.js → badge-k2vhtDqH.js} +1 -1
  286. package/static/dashboard/assets/collection-runtime-BFQ8teQJ.js +1 -0
  287. package/static/dashboard/assets/{dashboard-CD-FFVRD.js → dashboard-DWZ-fmCT.js} +4 -4
  288. package/static/dashboard/assets/dashboard-rk080mvG.css +1 -0
  289. package/static/dashboard/assets/dialog-1CCw-iHT.js +1 -0
  290. package/static/dashboard/assets/{download-nOP38LtW.js → download-C6-SNY1q.js} +1 -1
  291. package/static/dashboard/assets/{external-link-Bh2ewN9E.js → external-link-BgPcZ_j2.js} +1 -1
  292. package/static/dashboard/assets/index-6LcIUIYO.js +1 -0
  293. package/static/dashboard/assets/index-D7A8U6Pt.js +41 -0
  294. package/static/dashboard/assets/index-UD6QOE7H.js +4 -0
  295. package/static/dashboard/assets/{key-round-bGmguBLv.js → key-round-BOd7Jo0Q.js} +1 -1
  296. package/static/dashboard/assets/layout-DB3pMyTl.js +1 -0
  297. package/static/dashboard/assets/pod-collections-host-KnnGFZHL.js +8 -0
  298. package/static/dashboard/assets/{rotate-ccw-D2x__ODW.js → rotate-ccw-CCgJqsvx.js} +1 -1
  299. package/static/dashboard/assets/{skeleton-VOqft8WS.js → skeleton-Bc7abAn5.js} +1 -1
  300. package/static/dashboard/assets/{waypoints-fgRqm4oH.js → waypoints-s-opmJxx.js} +1 -1
  301. package/static/dashboard/assets/{workspace-layout-DV4dgeHX.js → workspace-layout-DqIQeTBV.js} +1 -1
  302. package/static/dashboard/dashboard.html +2 -2
  303. package/static/settings/assets/AiConfigContext-BB9KZySa.js +1 -0
  304. package/static/settings/assets/AiConfigPage-DGSXUkLs.js +1 -0
  305. package/static/settings/assets/DocumentProcessingPanel-Da2Re6Rg.js +1 -0
  306. package/static/settings/assets/IndexLifecyclePanel-BSZuIzgr.js +1 -0
  307. package/static/settings/assets/{IndexSubjectPanel-BgbI9c-a.js → IndexSubjectPanel-CFd0ay-r.js} +1 -1
  308. package/static/settings/assets/ModelAssignmentsPanel-CSAryvWJ.js +1 -0
  309. package/static/settings/assets/ModelsPage-BQwCwLHU.js +11 -0
  310. package/static/settings/assets/{NetworkPage-C8OA9Mtp.js → NetworkPage-BKymvsQc.js} +2 -2
  311. package/static/settings/assets/{PaneListHeader-COvlPJWj.js → PaneListHeader-Bjh3hRdQ.js} +1 -1
  312. package/static/settings/assets/SearchIndexingPanel-MjKL5QLU.js +1 -0
  313. package/static/settings/assets/{StatusSubjectPanel-BuLml7qn.js → StatusSubjectPanel-Bvl6K4mx.js} +1 -1
  314. package/static/settings/assets/StatusWorkspace-B0NzrP--.js +1 -0
  315. package/static/settings/assets/{SystemSettingsPage-DlzmaWV-.js → SystemSettingsPage-D2z6Z4FE.js} +1 -1
  316. package/static/{auth-callback/assets/SystemSettingsSubjectPanel-CDqRuC4t.js → settings/assets/SystemSettingsSubjectPanel-4sxma4X4.js} +5 -5
  317. package/static/settings/assets/XpodAiConnectionsPodStore-BkNQ8fZx.js +3 -0
  318. package/static/settings/assets/{activity-TLerXgMs.js → activity-CgJJGpjx.js} +1 -1
  319. package/static/settings/assets/ai-connections-CryuHGKW.js +1 -0
  320. package/static/settings/assets/{badge-Bcs8iWrH.js → badge-C4NypVyT.js} +1 -1
  321. package/static/settings/assets/collection-runtime-CTLCDxAi.js +1 -0
  322. package/static/settings/assets/dialog-Csq0vWUi.js +1 -0
  323. package/static/settings/assets/{download-Ct3QmHPt.js → download-DaLJJ-Wp.js} +1 -1
  324. package/static/settings/assets/{external-link-C8KiHbss.js → external-link--w_iW5Au.js} +1 -1
  325. package/static/settings/assets/index-B0mQa59b.js +4 -0
  326. package/static/settings/assets/index-BukHiu4y.js +1 -0
  327. package/static/settings/assets/index-COmVnZi2.js +41 -0
  328. package/static/settings/assets/{index-browser-CazX1pc2.js → index-browser-BGpvHwD0.js} +1 -1
  329. package/static/settings/assets/{key-round-B19YD1GA.js → key-round-Bk4YRjBu.js} +1 -1
  330. package/static/settings/assets/layout-DB3pMyTl.js +1 -0
  331. package/static/settings/assets/pod-collections-host-KnnGFZHL.js +8 -0
  332. package/static/settings/assets/{rotate-ccw-AQm2kMgU.js → rotate-ccw-TCabbqsr.js} +1 -1
  333. package/static/settings/assets/{settings-C-VBtt9r.js → settings-CJWpdbNC.js} +5 -5
  334. package/static/settings/assets/settings-rk080mvG.css +1 -0
  335. package/static/settings/assets/{skeleton-DyZ1OBYf.js → skeleton-JDHYVe7t.js} +1 -1
  336. package/static/settings/assets/{waypoints-CC3wq-SU.js → waypoints-DDvEl-MU.js} +1 -1
  337. package/static/settings/assets/{workspace-layout-BxXNKgf9.js → workspace-layout-DV5QU30U.js} +1 -1
  338. package/static/settings/settings.html +2 -2
  339. package/static/auth-callback/assets/AiConfigContext-DqWRSm7n.js +0 -1
  340. package/static/auth-callback/assets/AiConfigPage-C5YYyvzl.js +0 -1
  341. package/static/auth-callback/assets/DocumentProcessingPanel-DZCiacBz.js +0 -1
  342. package/static/auth-callback/assets/IndexLifecyclePanel-CCLeOljB.js +0 -1
  343. package/static/auth-callback/assets/ModelAssignmentsPanel-jUUTDKuh.js +0 -1
  344. package/static/auth-callback/assets/ModelsPage-B08zdtff.js +0 -10
  345. package/static/auth-callback/assets/SearchIndexingPanel-D4YRg1-8.js +0 -1
  346. package/static/auth-callback/assets/StatusWorkspace-Cp5LQ9Yx.js +0 -1
  347. package/static/auth-callback/assets/ai-connections-CHNTKemd.js +0 -1
  348. package/static/auth-callback/assets/auth-callback-DWMMiFjh.css +0 -1
  349. package/static/auth-callback/assets/index-DSNnLUbU.js +0 -4
  350. package/static/auth-callback/assets/index-FHhHoKgp.js +0 -41
  351. package/static/dashboard/assets/AiConfigContext-BpPDKSzI.js +0 -1
  352. package/static/dashboard/assets/AiConfigPage-wsqEZDIX.js +0 -1
  353. package/static/dashboard/assets/DocumentProcessingPanel-CDSy3juc.js +0 -1
  354. package/static/dashboard/assets/IndexLifecyclePanel-CVd6vQCX.js +0 -1
  355. package/static/dashboard/assets/ModelAssignmentsPanel-BeBZhnQd.js +0 -1
  356. package/static/dashboard/assets/ModelsPage-B6McSCJw.js +0 -25
  357. package/static/dashboard/assets/SearchIndexingPanel-CGs0OZ12.js +0 -1
  358. package/static/dashboard/assets/ai-connections-CHNTKemd.js +0 -1
  359. package/static/dashboard/assets/dashboard-DWMMiFjh.css +0 -1
  360. package/static/dashboard/assets/index-PgS-Bt7X.js +0 -41
  361. package/static/dashboard/assets/index-QN2QgEwr.js +0 -4
  362. package/static/settings/assets/AiConfigContext-zKQYdfai.js +0 -1
  363. package/static/settings/assets/AiConfigPage-Bs1EE3Ei.js +0 -1
  364. package/static/settings/assets/DocumentProcessingPanel-CSFfdOXS.js +0 -1
  365. package/static/settings/assets/IndexLifecyclePanel-BR9Su49B.js +0 -1
  366. package/static/settings/assets/ModelAssignmentsPanel-C0BE7nbk.js +0 -1
  367. package/static/settings/assets/ModelsPage-CU-YQ8_7.js +0 -10
  368. package/static/settings/assets/SearchIndexingPanel-Bdd46mIV.js +0 -1
  369. package/static/settings/assets/ai-connections-CHNTKemd.js +0 -1
  370. package/static/settings/assets/index-1Q2cRjZd.js +0 -41
  371. package/static/settings/assets/index-DBO_Vq0P.js +0 -4
  372. package/static/settings/assets/settings-DWMMiFjh.css +0 -1
@@ -0,0 +1,175 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * Presentation of a model catalog, shared by the provider pages and the Xpod
4
+ * keys page: both list models, so the search box, the inline placeholder and the
5
+ * model rows must stay one implementation.
6
+ */
7
+ /**
8
+ * Every capability a model row can show, and the glyph that carries it.
9
+ *
10
+ * This is the single definition of the vocabulary: a row renders a glyph for
11
+ * every token it is given, so a token without an entry here would leave a hole.
12
+ * `modelIconTokens` therefore filters through it. The two lists used to be
13
+ * maintained separately, which is how `embedding` and the non-image input
14
+ * modalities (`pdf`, `audio`, `video`) reached the row with nothing to draw.
15
+ */
16
+ export declare const MODEL_CAPABILITY_PRESENTATION: {
17
+ readonly image: {
18
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
19
+ readonly label: "视觉识别";
20
+ readonly className: "text-green-500";
21
+ };
22
+ readonly pdf: {
23
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
24
+ readonly label: "文档输入";
25
+ readonly className: "text-teal-500";
26
+ };
27
+ readonly audio: {
28
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
29
+ readonly label: "音频输入";
30
+ readonly className: "text-cyan-500";
31
+ };
32
+ readonly video: {
33
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
34
+ readonly label: "视频输入";
35
+ readonly className: "text-indigo-500";
36
+ };
37
+ readonly web: {
38
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
39
+ readonly label: "联网搜索";
40
+ readonly className: "text-blue-500";
41
+ };
42
+ readonly tool_call: {
43
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
44
+ readonly label: "函数调用";
45
+ readonly className: "text-orange-500";
46
+ };
47
+ readonly reasoning: {
48
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
49
+ readonly label: "推理";
50
+ readonly className: "text-purple-500";
51
+ };
52
+ readonly embedding: {
53
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
54
+ readonly label: "向量模型";
55
+ readonly className: "text-sky-500";
56
+ };
57
+ readonly fast: {
58
+ readonly icon: import("react").ForwardRefExoticComponent<Omit<import("lucide-react").LucideProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
59
+ readonly label: "快速模型";
60
+ readonly className: "text-amber-500";
61
+ };
62
+ };
63
+ export type ModelCapabilityToken = keyof typeof MODEL_CAPABILITY_PRESENTATION;
64
+ export declare function isPresentedCapability(token: string): token is ModelCapabilityToken;
65
+ /**
66
+ * Capability glyph shown beside a model name.
67
+ *
68
+ * Each glyph owns its `TooltipProvider` on purpose. Radix keeps one "pointer in
69
+ * transit" flag per provider and every trigger inside it ignores pointer moves
70
+ * while that flag is set, so a glyph whose tooltip is mid-transit would silently
71
+ * swallow the next hover of any other tooltip in the same provider - including
72
+ * the ⓘ next to the provider name. Isolating the glyphs keeps that state local.
73
+ */
74
+ export declare function CapabilityIcon({ type }: {
75
+ type: string;
76
+ }): import("react").JSX.Element | null;
77
+ /**
78
+ * Capability tokens for one model row.
79
+ *
80
+ * Both surfaces that list models fold the same two sources, and both used to
81
+ * build the list inline. Duplicates are folded too: a model whose catalog entry
82
+ * declares an `image` input modality *and* `imageInput: true` describes one
83
+ * capability, but the two sources would render it twice under one React key.
84
+ *
85
+ * Tokens outside the vocabulary are dropped here rather than handed to a glyph
86
+ * that would render nothing, so a row never carries a mark it cannot show.
87
+ */
88
+ export declare function modelIconTokens(model: {
89
+ inputModalities?: readonly string[];
90
+ capabilities?: readonly string[];
91
+ }): ModelCapabilityToken[];
92
+ /**
93
+ * The id a model is listed under, however the Pod stored it.
94
+ *
95
+ * A Pod persists a picked model as the resource it selected, so an entry can
96
+ * arrive with that resource reference where the model id belongs - typically a
97
+ * pinned selection whose document moved to another offering
98
+ * (`…/settings/providers/openai-official-subscription.ttl#gpt-6-astra`). The
99
+ * fragment names the model and the document only says where it is stored, so a
100
+ * reference folds down to the id it names instead of listing that model twice,
101
+ * once as `GPT-6-Astra` and once as a URL-shaped row.
102
+ *
103
+ * `@undefineds.co/models` owns this rule for Pod reads (`normalizeAIConfigModelId`
104
+ * in its `ai-config` module); the catalog repeats just the identity both lists
105
+ * fold on, because this package carries no Pod schema.
106
+ */
107
+ export declare function modelCatalogId(model: {
108
+ id: string;
109
+ }): string;
110
+ /** Fold one entry onto the model id both surfaces list it under. */
111
+ export declare function withCatalogModelId<T extends {
112
+ id: string;
113
+ }>(model: T): T;
114
+ /**
115
+ * Turns one model in a list on or off.
116
+ *
117
+ * A checkbox reads as "pick some of these" and says nothing about what picking
118
+ * does. This is a state with a consequence - the model joins the account's
119
+ * models, or it is withdrawn from the models list endpoint - so it uses the same
120
+ * affordance the credential rows already use for 启用/停用, and names the action.
121
+ */
122
+ export declare function AiModelEnableToggle({ enabled, disabled, label, onToggle, }: {
123
+ enabled: boolean;
124
+ disabled?: boolean;
125
+ /** The model name; the action verb is added around it. */
126
+ label: string;
127
+ onToggle(): void;
128
+ }): import("react").JSX.Element;
129
+ /** Magnifier-prefixed model search, sat on the right of a model list header. */
130
+ export declare function AiModelSearchInput({ value, onChange }: {
131
+ value: string;
132
+ onChange: (value: string) => void;
133
+ }): import("react").JSX.Element;
134
+ /** Inline placeholder used when a model list has nothing to show. */
135
+ export declare function AiModelEmptyPanel({ tone, children }: {
136
+ tone?: 'destructive';
137
+ children: ReactNode;
138
+ }): import("react").JSX.Element;
139
+ /**
140
+ * One model row, as every list of models renders it.
141
+ *
142
+ * This is the component both surfaces use. They used to assemble `AiModelTile`
143
+ * themselves - each with its own leading control, its own badges and its own
144
+ * spacing - which is exactly how one list drifted away from the other. A caller
145
+ * supplies the model, the switch state and any per-row extras; the row itself
146
+ * is decided here.
147
+ */
148
+ export declare function AiModelRow({ label, modelId, iconTokens, enabled, toggleDisabled, onToggle, onEdit, onDelete, unavailable, badges, }: {
149
+ label: string;
150
+ modelId?: string;
151
+ iconTokens?: string[];
152
+ /** The model is on: it is joined to the account and published to clients. */
153
+ enabled: boolean;
154
+ toggleDisabled?: boolean;
155
+ /** Omit for a list that cannot be switched. */
156
+ onToggle?: () => void;
157
+ onEdit?: () => void;
158
+ onDelete?: () => void;
159
+ unavailable?: boolean;
160
+ badges?: ReactNode;
161
+ }): import("react").JSX.Element;
162
+ /**
163
+ * One model row. `leading` carries the provider page's selection checkbox and
164
+ * `actions` its edit/delete buttons; a read-only list passes neither.
165
+ */
166
+ export declare function AiModelTile({ label, modelId, iconTokens, badges, leading, actions, selected, unavailable, }: {
167
+ label: string;
168
+ modelId?: string;
169
+ iconTokens?: string[];
170
+ badges?: ReactNode;
171
+ leading?: ReactNode;
172
+ actions?: ReactNode;
173
+ selected?: boolean;
174
+ unavailable?: boolean;
175
+ }): import("react").JSX.Element;
@@ -0,0 +1,133 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Input, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, cn, } from '@undefineds.co/shared-ui';
3
+ import { AudioLines, Box, Boxes, Brain, FileText, Globe, Image as ImageIcon, Pause, Pencil, Play, Search, Trash2, Video, Zap, } from 'lucide-react';
4
+ import { AiCopyButton } from './AiCopyButton.js';
5
+ import { AiRowAction } from './AiRowAction.js';
6
+ /**
7
+ * Presentation of a model catalog, shared by the provider pages and the Xpod
8
+ * keys page: both list models, so the search box, the inline placeholder and the
9
+ * model rows must stay one implementation.
10
+ */
11
+ /**
12
+ * Every capability a model row can show, and the glyph that carries it.
13
+ *
14
+ * This is the single definition of the vocabulary: a row renders a glyph for
15
+ * every token it is given, so a token without an entry here would leave a hole.
16
+ * `modelIconTokens` therefore filters through it. The two lists used to be
17
+ * maintained separately, which is how `embedding` and the non-image input
18
+ * modalities (`pdf`, `audio`, `video`) reached the row with nothing to draw.
19
+ */
20
+ export const MODEL_CAPABILITY_PRESENTATION = {
21
+ image: { icon: ImageIcon, label: '视觉识别', className: 'text-green-500' },
22
+ pdf: { icon: FileText, label: '文档输入', className: 'text-teal-500' },
23
+ audio: { icon: AudioLines, label: '音频输入', className: 'text-cyan-500' },
24
+ video: { icon: Video, label: '视频输入', className: 'text-indigo-500' },
25
+ web: { icon: Globe, label: '联网搜索', className: 'text-blue-500' },
26
+ tool_call: { icon: Box, label: '函数调用', className: 'text-orange-500' },
27
+ reasoning: { icon: Brain, label: '推理', className: 'text-purple-500' },
28
+ embedding: { icon: Boxes, label: '向量模型', className: 'text-sky-500' },
29
+ fast: { icon: Zap, label: '快速模型', className: 'text-amber-500' },
30
+ };
31
+ export function isPresentedCapability(token) {
32
+ return Object.prototype.hasOwnProperty.call(MODEL_CAPABILITY_PRESENTATION, token);
33
+ }
34
+ /**
35
+ * Capability glyph shown beside a model name.
36
+ *
37
+ * Each glyph owns its `TooltipProvider` on purpose. Radix keeps one "pointer in
38
+ * transit" flag per provider and every trigger inside it ignores pointer moves
39
+ * while that flag is set, so a glyph whose tooltip is mid-transit would silently
40
+ * swallow the next hover of any other tooltip in the same provider - including
41
+ * the ⓘ next to the provider name. Isolating the glyphs keeps that state local.
42
+ */
43
+ export function CapabilityIcon({ type }) {
44
+ if (!isPresentedCapability(type))
45
+ return null;
46
+ const capability = MODEL_CAPABILITY_PRESENTATION[type];
47
+ const Icon = capability.icon;
48
+ return (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": capability.label, className: "flex cursor-help items-center justify-center rounded-sm opacity-80 transition-opacity hover:opacity-100 focus:outline-none focus-visible:opacity-100", children: _jsx(Icon, { "aria-hidden": "true", className: cn('h-3.5 w-3.5', capability.className) }) }) }), _jsx(TooltipContent, { children: capability.label })] }) }));
49
+ }
50
+ /**
51
+ * Capability tokens for one model row.
52
+ *
53
+ * Both surfaces that list models fold the same two sources, and both used to
54
+ * build the list inline. Duplicates are folded too: a model whose catalog entry
55
+ * declares an `image` input modality *and* `imageInput: true` describes one
56
+ * capability, but the two sources would render it twice under one React key.
57
+ *
58
+ * Tokens outside the vocabulary are dropped here rather than handed to a glyph
59
+ * that would render nothing, so a row never carries a mark it cannot show.
60
+ */
61
+ export function modelIconTokens(model) {
62
+ return [...new Set([
63
+ ...(model.inputModalities ?? []).filter((modality) => modality !== 'text'),
64
+ ...(model.capabilities ?? []),
65
+ ])].filter(isPresentedCapability);
66
+ }
67
+ /**
68
+ * The id a model is listed under, however the Pod stored it.
69
+ *
70
+ * A Pod persists a picked model as the resource it selected, so an entry can
71
+ * arrive with that resource reference where the model id belongs - typically a
72
+ * pinned selection whose document moved to another offering
73
+ * (`…/settings/providers/openai-official-subscription.ttl#gpt-6-astra`). The
74
+ * fragment names the model and the document only says where it is stored, so a
75
+ * reference folds down to the id it names instead of listing that model twice,
76
+ * once as `GPT-6-Astra` and once as a URL-shaped row.
77
+ *
78
+ * `@undefineds.co/models` owns this rule for Pod reads (`normalizeAIConfigModelId`
79
+ * in its `ai-config` module); the catalog repeats just the identity both lists
80
+ * fold on, because this package carries no Pod schema.
81
+ */
82
+ export function modelCatalogId(model) {
83
+ const fragmentIndex = model.id.lastIndexOf('#');
84
+ return fragmentIndex >= 0 ? model.id.slice(fragmentIndex + 1) : model.id;
85
+ }
86
+ /** Fold one entry onto the model id both surfaces list it under. */
87
+ export function withCatalogModelId(model) {
88
+ const id = modelCatalogId(model);
89
+ return id === model.id ? model : { ...model, id };
90
+ }
91
+ /**
92
+ * Turns one model in a list on or off.
93
+ *
94
+ * A checkbox reads as "pick some of these" and says nothing about what picking
95
+ * does. This is a state with a consequence - the model joins the account's
96
+ * models, or it is withdrawn from the models list endpoint - so it uses the same
97
+ * affordance the credential rows already use for 启用/停用, and names the action.
98
+ */
99
+ export function AiModelEnableToggle({ enabled, disabled = false, label, onToggle, }) {
100
+ return (_jsx(AiRowAction, { label: `${enabled ? '停用' : '启用'} ${label}`, disabled: disabled, onClick: onToggle, children: enabled
101
+ ? _jsx(Pause, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
102
+ : _jsx(Play, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) }));
103
+ }
104
+ /** Magnifier-prefixed model search, sat on the right of a model list header. */
105
+ export function AiModelSearchInput({ value, onChange }) {
106
+ return (_jsxs("div", { className: "relative w-full sm:w-auto", children: [_jsx(Search, { "aria-hidden": "true", className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: value, onChange: (event) => onChange(event.target.value), placeholder: "\u641C\u7D22\u6A21\u578B...", className: "h-8 w-full bg-background pl-8 text-xs sm:w-[232px]", autoComplete: "off", "data-lpignore": "true", "data-1p-ignore": true })] }));
107
+ }
108
+ /** Inline placeholder used when a model list has nothing to show. */
109
+ export function AiModelEmptyPanel({ tone, children }) {
110
+ return (_jsx("div", { className: "flex items-center gap-2 py-6 text-sm text-muted-foreground", children: tone === 'destructive' ? _jsx("p", { className: "text-destructive", children: children }) : children }));
111
+ }
112
+ /**
113
+ * One model row, as every list of models renders it.
114
+ *
115
+ * This is the component both surfaces use. They used to assemble `AiModelTile`
116
+ * themselves - each with its own leading control, its own badges and its own
117
+ * spacing - which is exactly how one list drifted away from the other. A caller
118
+ * supplies the model, the switch state and any per-row extras; the row itself
119
+ * is decided here.
120
+ */
121
+ export function AiModelRow({ label, modelId, iconTokens = [], enabled, toggleDisabled = false, onToggle, onEdit, onDelete, unavailable = false, badges, }) {
122
+ // The action order is the credential rows' order - edit, 启用/停用, delete - so
123
+ // the same gesture sits in the same place wherever a row is switched on or off.
124
+ const hasActions = Boolean(onToggle || onEdit || onDelete);
125
+ return (_jsx(AiModelTile, { label: label, modelId: modelId, iconTokens: iconTokens, selected: enabled, unavailable: unavailable, badges: badges, actions: hasActions ? (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [onEdit ? (_jsx(AiRowAction, { label: `编辑 ${label}`, disabled: toggleDisabled, onClick: onEdit, children: _jsx(Pencil, { "aria-hidden": "true", className: "h-3.5 w-3.5 text-muted-foreground" }) })) : null, onToggle ? (_jsx(AiModelEnableToggle, { enabled: enabled, disabled: toggleDisabled, label: label, onToggle: onToggle })) : null, onDelete ? (_jsx(AiRowAction, { label: `删除 ${label}`, disabled: toggleDisabled, onClick: onDelete, children: _jsx(Trash2, { "aria-hidden": "true", className: "h-3.5 w-3.5 text-muted-foreground" }) })) : null] })) : undefined }));
126
+ }
127
+ /**
128
+ * One model row. `leading` carries the provider page's selection checkbox and
129
+ * `actions` its edit/delete buttons; a read-only list passes neither.
130
+ */
131
+ export function AiModelTile({ label, modelId, iconTokens = [], badges, leading, actions, selected = false, unavailable = false, }) {
132
+ return (_jsxs("div", { className: cn('group flex items-center gap-3 rounded-lg border bg-card p-3 transition-all duration-200 hover:border-border/60 hover:bg-accent/30', selected ? 'border-primary/40 bg-primary/[0.03]' : 'border-border/40', unavailable && 'opacity-75'), children: [leading, _jsx("div", { className: "shrink-0 rounded bg-muted/50 p-2 text-muted-foreground transition-colors group-hover:text-primary", children: _jsx(Box, { "aria-hidden": "true", className: "h-4 w-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate text-sm font-medium text-foreground/90", children: label }), _jsx("div", { className: "flex items-center gap-1", children: iconTokens.map((token) => _jsx(CapabilityIcon, { type: token }, token)) }), badges] }), modelId && modelId !== label ? (_jsxs("div", { className: "mt-0.5 flex items-center gap-1.5", children: [_jsx("code", { className: "max-w-[300px] truncate font-mono text-[10px] text-muted-foreground opacity-70", children: modelId }), _jsx(AiCopyButton, { value: modelId, label: `${label} ID`, title: "\u590D\u5236 ID", variant: "ghost", size: "icon", className: "h-4 w-4 rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:text-foreground group-hover:opacity-100 group-focus-within:opacity-100 focus:opacity-100", iconClassName: "h-3 w-3", copiedIconClassName: "text-primary" })] })) : null] }), actions] }));
133
+ }
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { authMethodLabel, offeringKindLabel, offeringTitle } from './offering-label.js';
3
3
  import { authorizationMethodsForOffering } from './authorization-methods.js';
4
- import { endpointDisplayValue, endpointProtocolLabel } from './offering-endpoints.js';
4
+ import { AiEndpointList } from './AiEndpointList.js';
5
5
  export function AiOfferingDetails({ offering, methods }) {
6
6
  const endpoints = offering.endpoints ?? [];
7
7
  const title = offeringTitle(offering);
@@ -9,7 +9,7 @@ export function AiOfferingDetails({ offering, methods }) {
9
9
  const subtitle = kindLabel && kindLabel !== title ? [kindLabel] : [];
10
10
  return (_jsxs("section", { className: "space-y-2", "aria-labelledby": `offering-${offering.id}`, children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h4", { id: `offering-${offering.id}`, className: "text-sm font-medium text-foreground", children: title }), subtitle.length ? (_jsx("div", { className: "mt-1 flex items-center gap-2 text-xs text-muted-foreground", children: subtitle.map((part) => _jsx("span", { children: part }, part)) })) : null] }), _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-x-3 gap-y-1 text-xs", children: [_jsx("span", { className: "text-muted-foreground", children: authMethodLabel(offering, methods) }), offering.consoleUrl ? _jsx(OfferingLink, { href: offering.consoleUrl, label: "\u63A7\u5236\u53F0" }) : null, offering.subscriptionUrl ? _jsx(OfferingLink, { href: offering.subscriptionUrl, label: "\u8BA2\u9605\u4E0E\u8D26\u5355" }) : null, offering.quota?.url ? _jsx(OfferingLink, { href: offering.quota.url, label: "\u989D\u5EA6\u4E0E\u7528\u91CF" }) : null, offering.usagePolicyUrl ? _jsx(OfferingLink, { href: offering.usagePolicyUrl, label: "\u4F7F\u7528\u653F\u7B56" }) : null] })] }), offering.lifecycle === 'unavailable' && !authorizationMethodsForOffering(offering).some((method) => method.lifecycle === 'active') ? (_jsx("p", { className: "text-xs text-muted-foreground", children: offering.kind === 'oauth-subscription'
11
11
  ? '暂不可用:账号订阅需在 Xpod 桌面版中导入本机客户端(如 Codex CLI)的登录态,浏览器中无法完成。'
12
- : '暂不可用:该接入方式尚未提供可用的连接流程。' })) : null, endpoints.length ? (_jsx("dl", { className: "space-y-1 text-[11px] text-muted-foreground", children: endpoints.map((endpoint) => (_jsxs("div", { className: "flex min-w-0 items-baseline gap-2", children: [_jsx("dt", { className: "shrink-0 text-foreground/70", children: endpointProtocolLabel(endpoint.protocol) }), _jsx("dd", { className: "min-w-0 truncate font-mono", title: endpoint.baseUrl, children: endpointDisplayValue(endpoint.baseUrl) })] }, `${endpoint.protocol}:${endpoint.baseUrl}`))) })) : null] }));
12
+ : '暂不可用:该接入方式尚未提供可用的连接流程。' })) : null, endpoints.length ? _jsx(AiEndpointList, { endpoints: endpoints }) : null] }));
13
13
  }
14
14
  function OfferingLink({ href, label }) {
15
15
  return _jsx("a", { href: href, target: "_blank", rel: "noreferrer", className: "text-primary hover:underline", children: label });
@@ -1,9 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useMemo, useState } from 'react';
3
- import { Avatar, AvatarFallback, AvatarImage, Badge, Button, Input, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, cn, } from '@undefineds.co/shared-ui';
3
+ import { Badge, Button, TooltipProvider, } from '@undefineds.co/shared-ui';
4
4
  import { getProviderAvatar, getProviderAvatarBackground } from './provider-visuals.js';
5
- import { Box, Brain, Check, Copy, ExternalLink, Globe, Image as ImageIcon, Info, Loader2, Pencil, Plus, RotateCw, Search, Trash2, } from 'lucide-react';
5
+ import { Box, Check, Loader2, Plus, RotateCw, } from 'lucide-react';
6
+ import { AiProviderHeader } from './AiProviderHeader.js';
6
7
  import { AiCredentialPoolSection, } from './AiCredentialPoolSection.js';
8
+ import { AiModelEmptyPanel, AiModelSearchInput, AiModelRow, modelIconTokens, } from './AiModelCatalog.js';
7
9
  export function AiProviderCard({ definition, product, status, accountLabel, attempt, attemptOfferingId, apiKey, baseUrl = '', busy, disabled = false, error, quotas, models, verifyPending = false, onApiKeyChange, onBaseUrlChange, onBeginApiKey, onBeginOffering, onCancelConnect, onBeginBrowser, onSaveApiKey, onDisconnect, onCreateApiKeyCredential, onCreateLocalCredential, onUpdateCredential, onDeleteCredential, onTestCredential, onReorderCredentials, onRefreshQuota, onVerify, onAddModel, onEditModel, onDeleteModel, selectedModelIds, modelSelectionStatus, onModelSelectionChange, onDismissError, }) {
8
10
  const isConfigured = status === 'configured';
9
11
  const isConnected = status === 'connected';
@@ -11,7 +13,6 @@ export function AiProviderCard({ definition, product, status, accountLabel, atte
11
13
  ? error.message
12
14
  : undefined;
13
15
  const [modelSearch, setModelSearch] = useState('');
14
- const [copiedModelId, setCopiedModelId] = useState();
15
16
  const [localSelectedModelIds, setLocalSelectedModelIds] = useState(selectedModelIds ?? []);
16
17
  const effectiveSelectedModelIds = selectedModelIds ?? localSelectedModelIds;
17
18
  const catalog = useMemo(() => aggregateProviderModels(models), [models]);
@@ -22,22 +23,8 @@ export function AiProviderCard({ definition, product, status, accountLabel, atte
22
23
  return catalog;
23
24
  return catalog.filter((model) => model.searchText.includes(query));
24
25
  }, [catalog, modelSearch]);
25
- const selectableVisibleModels = visibleModels.filter((model) => model.availability !== 'unavailable');
26
- const selectedVisibleCount = selectableVisibleModels.filter(isModelSelected).length;
27
- const allVisibleSelected = selectableVisibleModels.length > 0 && selectedVisibleCount === selectableVisibleModels.length;
28
- const someVisibleSelected = selectedVisibleCount > 0 && !allVisibleSelected;
29
26
  const selectedModelCount = catalog.filter(isModelSelected).length;
30
27
  const unavailableModelCount = catalog.filter((model) => model.availability === 'unavailable').length;
31
- const copyModelId = async (modelId) => {
32
- try {
33
- await navigator.clipboard.writeText(modelId);
34
- setCopiedModelId(modelId);
35
- setTimeout(() => setCopiedModelId((current) => (current === modelId ? undefined : current)), 1500);
36
- }
37
- catch {
38
- setCopiedModelId(undefined);
39
- }
40
- };
41
28
  const toggleModel = (model) => {
42
29
  const next = new Set(effectiveSelectedModelIds);
43
30
  const remove = isModelSelected(model);
@@ -52,56 +39,18 @@ export function AiProviderCard({ definition, product, status, accountLabel, atte
52
39
  setLocalSelectedModelIds(nextModelIds);
53
40
  onModelSelectionChange?.(definition.id, nextModelIds);
54
41
  };
55
- const toggleVisibleModels = () => {
56
- const next = new Set(effectiveSelectedModelIds);
57
- for (const model of selectableVisibleModels) {
58
- for (const id of allVisibleSelected ? model.selectionIds : model.availableSelectionIds) {
59
- if (allVisibleSelected)
60
- next.delete(id);
61
- else
62
- next.add(id);
63
- }
64
- }
65
- const nextModelIds = [...next];
66
- if (selectedModelIds === undefined)
67
- setLocalSelectedModelIds(nextModelIds);
68
- onModelSelectionChange?.(definition.id, nextModelIds);
69
- };
70
- return (_jsx(TooltipProvider, { children: _jsxs("div", { className: "space-y-8", children: [_jsxs("header", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Avatar, { className: "h-9 w-9 shrink-0 rounded-lg border border-border/50 bg-muted/50 shadow-sm", style: getProviderAvatarBackground(definition.id) ? { backgroundColor: getProviderAvatarBackground(definition.id) } : undefined, children: [_jsx(AvatarImage, { src: getProviderAvatar(definition.id), className: "object-cover" }), _jsx(AvatarFallback, { className: "rounded-lg bg-transparent text-sm font-bold uppercase text-muted-foreground", children: providerMark(definition.id) })] }), _jsxs("div", { className: "flex flex-col justify-center gap-0.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h2", { className: "text-base font-semibold leading-none tracking-tight text-foreground", children: definition.name }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": "\u63D0\u4F9B\u5546\u8BF4\u660E", className: "cursor-help rounded-sm text-muted-foreground/50 focus:outline-none focus-visible:text-foreground", children: _jsx(Info, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) }) }), _jsxs(TooltipContent, { className: "max-w-xs space-y-2 text-xs", children: [_jsx("p", { children: definition.description }), _jsx("p", { children: "Provider \u51ED\u8BC1\u4FDD\u5B58\u5728\u5F53\u524D Pod\uFF0C\u7531 Pod \u6743\u9650\u4FDD\u62A4\u3002" })] })] })] }), _jsxs("a", { href: definition.homeUrl, target: "_blank", rel: "noreferrer", className: "flex items-center gap-0.5 text-[10px] leading-none text-muted-foreground transition-colors hover:text-primary", children: ["\u8BBF\u95EE\u5B98\u7F51 ", _jsx(ExternalLink, { "aria-hidden": "true", className: "h-2.5 w-2.5" })] })] })] }), _jsx(Badge, { variant: isConnected || isConfigured ? 'default' : 'secondary', children: connectionStatusLabel(status) })] }), _jsx(AiCredentialPoolSection, { definition: definition, product: product, status: status, accountLabel: accountLabel, attempt: attempt, attemptOfferingId: attemptOfferingId, apiKey: apiKey, baseUrl: baseUrl, busy: busy, disabled: disabled, error: error, suppressError: Boolean(catalogError), quotas: quotas, onApiKeyChange: onApiKeyChange, onBaseUrlChange: onBaseUrlChange, onBeginApiKey: onBeginApiKey, onBeginOffering: onBeginOffering, onCancelConnect: onCancelConnect, onBeginBrowser: onBeginBrowser, onSaveApiKey: onSaveApiKey, onDisconnect: onDisconnect, onCreateApiKeyCredential: product?.offerings.length ? onCreateApiKeyCredential : undefined, onCreateLocalCredential: product?.offerings.length ? onCreateLocalCredential : undefined, onUpdateCredential: onUpdateCredential, onDeleteCredential: onDeleteCredential, onTestCredential: onTestCredential, onReorderCredentials: onReorderCredentials, onRefreshQuota: onRefreshQuota, onDismissError: onDismissError }), _jsxs("section", { className: "space-y-4", children: [_jsxs("div", { "data-testid": "provider-models-header", className: "flex flex-col gap-3 border-b border-border/40 pb-4 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [_jsx(Box, { className: "h-4 w-4 shrink-0 text-primary" }), _jsx("h3", { className: "shrink-0 text-sm font-medium text-foreground/90", children: "\u53EF\u7528\u6A21\u578B" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["\u5171 ", catalog.length, " \u00B7 \u5DF2\u52A0\u5165 ", selectedModelCount, " \u00B7 \u5DF2\u5931\u6548 ", unavailableModelCount] }), _jsxs("span", { role: "status", "aria-live": "polite", "aria-atomic": "true", className: cn('inline-flex items-center gap-1 text-xs', modelSelectionStatus === 'error' ? 'text-destructive' : 'text-muted-foreground'), children: [modelSelectionStatus === 'saving'
71
- ? _jsx(Loader2, { "aria-hidden": "true", className: "h-3 w-3 animate-spin motion-reduce:animate-none" })
72
- : modelSelectionStatus === 'saved' ? _jsx(Check, { "aria-hidden": "true", className: "h-3 w-3" }) : null, modelSelectionStatus === 'saving' ? '保存中…'
73
- : modelSelectionStatus === 'saved' ? '已保存'
74
- : modelSelectionStatus === 'error' ? '保存失败,请重试' : '选择后自动保存'] })] }), _jsxs("div", { "data-testid": "provider-models-actions", className: "flex w-full flex-wrap items-center gap-2 sm:w-auto sm:flex-nowrap sm:justify-end", children: [(isConfigured || isConnected) && (onVerify || onAddModel) ? (_jsxs(_Fragment, { children: [onAddModel ? (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", disabled: disabled || busy, onClick: onAddModel, children: [_jsx(Plus, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), "\u6DFB\u52A0\u6A21\u578B"] })) : null, onVerify && models.length > 0 ? (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", disabled: disabled || busy || verifyPending, onClick: onVerify, children: [verifyPending
42
+ return (_jsx(TooltipProvider, { children: _jsxs("div", { className: "space-y-8", children: [_jsx(AiProviderHeader, { name: definition.name, mark: providerMark(definition.id), avatar: getProviderAvatar(definition.id), avatarBackground: getProviderAvatarBackground(definition.id), infoLabel: "\u63D0\u4F9B\u5546\u8BF4\u660E", infoLines: [definition.description, 'Provider 凭证保存在当前 Pod,由 Pod 权限保护。'], link: { href: definition.homeUrl, label: '访问官网' }, badge: (_jsx(Badge, { variant: isConnected || isConfigured ? 'default' : 'secondary', children: connectionStatusLabel(status) })) }), _jsx(AiCredentialPoolSection, { definition: definition, product: product, status: status, accountLabel: accountLabel, attempt: attempt, attemptOfferingId: attemptOfferingId, apiKey: apiKey, baseUrl: baseUrl, busy: busy, disabled: disabled, error: error, suppressError: Boolean(catalogError), quotas: quotas, onApiKeyChange: onApiKeyChange, onBaseUrlChange: onBaseUrlChange, onBeginApiKey: onBeginApiKey, onBeginOffering: onBeginOffering, onCancelConnect: onCancelConnect, onBeginBrowser: onBeginBrowser, onSaveApiKey: onSaveApiKey, onDisconnect: onDisconnect, onCreateApiKeyCredential: product?.offerings.length ? onCreateApiKeyCredential : undefined, onCreateLocalCredential: product?.offerings.length ? onCreateLocalCredential : undefined, onUpdateCredential: onUpdateCredential, onDeleteCredential: onDeleteCredential, onTestCredential: onTestCredential, onReorderCredentials: onReorderCredentials, onRefreshQuota: onRefreshQuota, onDismissError: onDismissError }), _jsxs("section", { className: "space-y-8", children: [_jsxs("div", { "data-testid": "provider-models-header", className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [_jsxs("h3", { className: "flex items-center gap-2 text-sm font-medium text-foreground/90", children: [_jsx(Box, { "aria-hidden": "true", className: "h-4 w-4 text-primary" }), "\u53EF\u7528\u6A21\u578B"] }), _jsxs("span", { className: "text-xs text-muted-foreground", children: ["\u5171 ", catalog.length, " \u00B7 \u5DF2\u52A0\u5165 ", selectedModelCount, " \u00B7 \u5DF2\u5931\u6548 ", unavailableModelCount] }), modelSelectionStatus === 'saving' ? (_jsxs("span", { role: "status", "aria-live": "polite", "aria-atomic": "true", className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Loader2, { "aria-hidden": "true", className: "h-3 w-3 animate-spin motion-reduce:animate-none" }), "\u4FDD\u5B58\u4E2D\u2026"] })) : null, modelSelectionStatus === 'saved' ? (_jsxs("span", { role: "status", "aria-live": "polite", "aria-atomic": "true", className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Check, { "aria-hidden": "true", className: "h-3 w-3" }), "\u5DF2\u4FDD\u5B58"] })) : null] }), _jsxs("div", { "data-testid": "provider-models-actions", className: "flex w-full flex-wrap items-center gap-2 sm:w-auto sm:flex-nowrap sm:justify-end", children: [(isConfigured || isConnected) && (onVerify || onAddModel) ? (_jsxs(_Fragment, { children: [onAddModel ? (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", disabled: disabled || busy, onClick: onAddModel, children: [_jsx(Plus, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), "\u6DFB\u52A0\u6A21\u578B"] })) : null, onVerify && models.length > 0 ? (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", disabled: disabled || busy || verifyPending, onClick: onVerify, children: [verifyPending
75
43
  ? _jsx(Loader2, { "aria-hidden": "true", className: "h-3.5 w-3.5 animate-spin" })
76
44
  : _jsx(RotateCw, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), verifyPending ? '同步中...' : '刷新模型'] })) : onVerify ? (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", disabled: disabled || busy || verifyPending, onClick: onVerify, children: [verifyPending
77
45
  ? _jsx(Loader2, { "aria-hidden": "true", className: "h-3.5 w-3.5 animate-spin" })
78
- : _jsx(RotateCw, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), verifyPending ? '同步中...' : '同步模型'] })) : null] })) : null, _jsxs("div", { className: "relative w-full sm:w-auto", children: [_jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { value: modelSearch, onChange: (event) => setModelSearch(event.target.value), placeholder: "\u641C\u7D22\u6A21\u578B...", className: "h-8 w-full bg-background pl-8 text-xs sm:w-[232px]", autoComplete: "off", "data-lpignore": "true", "data-1p-ignore": true })] })] })] }), models.length === 0 ? (_jsx("div", { className: "rounded-lg border border-dashed border-border/50 bg-muted/5 py-12 text-center text-sm text-muted-foreground", children: catalogError ? (_jsx("p", { className: "text-destructive", children: catalogError })) : ('暂无可用模型') })) : visibleModels.length === 0 ? (_jsx("div", { className: "rounded-lg border border-dashed border-border/50 bg-muted/5 py-12 text-center text-sm text-muted-foreground", children: "\u672A\u627E\u5230\u5339\u914D\u7684\u6A21\u578B" })) : (_jsxs("div", { className: "grid gap-2", children: [_jsx("div", { className: "flex items-center justify-between px-1 py-1", children: _jsxs("button", { type: "button", role: "checkbox", "aria-label": "\u5168\u9009\u5F53\u524D\u7ED3\u679C", "aria-checked": someVisibleSelected ? 'mixed' : allVisibleSelected, disabled: disabled || busy || selectableVisibleModels.length === 0, onClick: toggleVisibleModels, className: "flex items-center gap-2 text-xs text-muted-foreground disabled:opacity-50", children: [_jsx("span", { className: cn('flex h-4 w-4 items-center justify-center rounded border', allVisibleSelected || someVisibleSelected ? 'border-primary bg-primary text-primary-foreground' : 'border-border'), children: someVisibleSelected ? _jsx("span", { "aria-hidden": "true", children: "\u2212" }) : _jsx(Check, { "aria-hidden": "true", className: cn('h-3 w-3', !allVisibleSelected && 'invisible') }) }), "\u5168\u9009\u5F53\u524D\u7ED3\u679C"] }) }), visibleModels.map((model) => {
79
- const selectionId = modelSelectionId(model);
80
- const isSelected = isModelSelected(model);
81
- const isUnavailable = model.availability === 'unavailable';
82
- const modelLabel = model.displayName ?? model.id;
83
- const iconTokens = [
84
- ...(model.inputModalities ?? []).filter((modality) => modality !== 'text'),
85
- ...(model.capabilities ?? []),
86
- ];
87
- return (_jsxs("div", { className: cn('group flex items-center gap-3 rounded-lg border bg-card p-3 transition-all duration-200 hover:border-border/60 hover:bg-accent/30', isSelected ? 'border-primary/40 bg-primary/[0.03]' : 'border-border/40', isUnavailable && 'opacity-75'), children: [_jsx("button", { type: "button", role: "checkbox", "aria-checked": isSelected, "aria-label": `${isSelected ? '取消选择' : '选择'} ${modelLabel}`, className: cn('flex h-5 w-5 shrink-0 items-center justify-center rounded border transition-colors focus:outline-none focus-visible:border-ring', isSelected
88
- ? 'border-primary bg-primary text-primary-foreground'
89
- : 'border-border text-transparent hover:border-primary/60'), disabled: disabled || busy || (isUnavailable && !isSelected), onClick: () => toggleModel(model), children: _jsx(Check, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) }), _jsx("div", { className: "shrink-0 rounded bg-muted/50 p-2 text-muted-foreground transition-colors group-hover:text-primary", children: _jsx(Box, { className: "h-4 w-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "truncate text-sm font-medium text-foreground/90", children: modelLabel }), _jsx("div", { className: "flex items-center gap-1", children: iconTokens.map((token) => _jsx(CapabilityIcon, { type: token }, token)) }), model.custom ? _jsx(Badge, { variant: "outline", className: "shrink-0 text-[10px] font-normal", children: "\u624B\u5DE5" }) : null, isUnavailable ? (_jsx(Badge, { variant: "destructive", className: "shrink-0 text-[10px] font-normal", children: "\u5DF2\u5931\u6548" })) : null] }), model.displayName ? (_jsxs("div", { className: "mt-0.5 flex items-center gap-1.5", children: [_jsx("code", { className: "max-w-[300px] truncate font-mono text-[10px] text-muted-foreground opacity-70", children: model.id }), _jsx("button", { type: "button", onClick: () => void copyModelId(model.id), className: "rounded p-0.5 text-muted-foreground opacity-0 transition-opacity hover:bg-muted hover:text-foreground group-hover:opacity-100 group-focus-within:opacity-100 focus:opacity-100", "aria-label": `复制 ${model.displayName} ID`, title: "\u590D\u5236 ID", children: copiedModelId === model.id
90
- ? _jsx(Check, { "aria-hidden": "true", className: "h-3 w-3 text-primary" })
91
- : _jsx(Copy, { "aria-hidden": "true", className: "h-3 w-3" }) })] })) : null] }), model.custom && (onEditModel || onDeleteModel) ? (_jsxs("div", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 focus-within:opacity-100", children: [onEditModel ? (_jsx(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", "aria-label": `编辑 ${model.displayName ?? model.id}`, onClick: () => onEditModel(model), children: _jsx(Pencil, { "aria-hidden": "true", className: "h-3.5 w-3.5 text-muted-foreground" }) })) : null, onDeleteModel ? (_jsx(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", "aria-label": `删除 ${model.displayName ?? model.id}`, onClick: () => onDeleteModel(model), children: _jsx(Trash2, { "aria-hidden": "true", className: "h-3.5 w-3.5 text-muted-foreground" }) })) : null] })) : null] }, selectionId));
92
- })] }))] })] }) }));
93
- }
94
- function CapabilityIcon({ type }) {
95
- const capability = {
96
- image: { icon: ImageIcon, label: '视觉识别', className: 'text-green-500' },
97
- web: { icon: Globe, label: '联网搜索', className: 'text-blue-500' },
98
- tool_call: { icon: Box, label: '函数调用', className: 'text-orange-500' },
99
- reasoning: { icon: Brain, label: '推理', className: 'text-purple-500' },
100
- }[type];
101
- if (!capability)
102
- return null;
103
- const Icon = capability.icon;
104
- return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": capability.label, className: "flex cursor-help items-center justify-center rounded-sm opacity-80 transition-opacity hover:opacity-100 focus:outline-none focus-visible:opacity-100", children: _jsx(Icon, { "aria-hidden": "true", className: cn('h-3.5 w-3.5', capability.className) }) }) }), _jsx(TooltipContent, { children: capability.label })] }));
46
+ : _jsx(RotateCw, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), verifyPending ? '同步中...' : '同步模型'] })) : null] })) : null, _jsx(AiModelSearchInput, { value: modelSearch, onChange: setModelSearch })] })] }), models.length === 0 ? (_jsx(AiModelEmptyPanel, { tone: catalogError ? 'destructive' : undefined, children: catalogError ?? '暂无可用模型' })) : visibleModels.length === 0 ? (_jsx(AiModelEmptyPanel, { children: "\u672A\u627E\u5230\u5339\u914D\u7684\u6A21\u578B" })) : (_jsx("div", { className: "grid gap-2", children: visibleModels.map((model) => {
47
+ const selectionId = modelSelectionId(model);
48
+ const isSelected = isModelSelected(model);
49
+ const isUnavailable = model.availability === 'unavailable';
50
+ const modelLabel = model.displayName ?? model.id;
51
+ const iconTokens = modelIconTokens(model);
52
+ return (_jsx(AiModelRow, { label: modelLabel, modelId: model.id, iconTokens: iconTokens, enabled: isSelected, toggleDisabled: disabled || busy || (isUnavailable && !isSelected), onToggle: () => toggleModel(model), onEdit: model.custom && onEditModel ? () => onEditModel(model) : undefined, onDelete: model.custom && onDeleteModel ? () => onDeleteModel(model) : undefined, unavailable: isUnavailable, badges: (_jsxs(_Fragment, { children: [model.custom ? _jsx(Badge, { variant: "outline", className: "shrink-0 text-[10px] font-normal", children: "\u624B\u5DE5" }) : null, isUnavailable ? (_jsx(Badge, { variant: "destructive", className: "shrink-0 text-[10px] font-normal", children: "\u5DF2\u5931\u6548" })) : null] })) }, selectionId));
53
+ }) }))] })] }) }));
105
54
  }
106
55
  function providerMark(provider) {
107
56
  switch (provider) {
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface AiProviderHeaderLink {
3
+ href: string;
4
+ label: string;
5
+ }
6
+ /**
7
+ * Header shared by every provider-style page: the provider pages and the Xpod
8
+ * API KEYS page differ only in mark, copy, link and badge, so the mark/name/ⓘ
9
+ * anatomy lives here once instead of being re-typed per page.
10
+ */
11
+ export declare function AiProviderHeader({ name, mark, avatar, avatarBackground, infoLabel, infoLines, link, badge, }: {
12
+ name: string;
13
+ /** Initials shown while (or instead of) the mark image. */
14
+ mark: string;
15
+ avatar?: string;
16
+ avatarBackground?: string;
17
+ infoLabel: string;
18
+ infoLines: ReactNode[];
19
+ link: AiProviderHeaderLink;
20
+ badge?: ReactNode;
21
+ }): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Avatar, AvatarFallback, AvatarImage, } from '@undefineds.co/shared-ui';
3
+ import { ExternalLink } from 'lucide-react';
4
+ import { AiInfoTooltip } from './AiInfoTooltip.js';
5
+ /**
6
+ * Header shared by every provider-style page: the provider pages and the Xpod
7
+ * API KEYS page differ only in mark, copy, link and badge, so the mark/name/ⓘ
8
+ * anatomy lives here once instead of being re-typed per page.
9
+ */
10
+ export function AiProviderHeader({ name, mark, avatar, avatarBackground, infoLabel, infoLines, link, badge, }) {
11
+ return (_jsxs("header", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Avatar, { className: "h-9 w-9 shrink-0 rounded-lg border border-border/50 bg-muted/50 shadow-sm", style: avatarBackground ? { backgroundColor: avatarBackground } : undefined, children: [_jsx(AvatarImage, { src: avatar, className: "object-cover" }), _jsx(AvatarFallback, { className: "rounded-lg bg-transparent text-sm font-bold uppercase text-muted-foreground", children: mark })] }), _jsxs("div", { className: "flex flex-col justify-center gap-0.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h2", { className: "text-base font-semibold leading-none tracking-tight text-foreground", children: name }), _jsx(AiInfoTooltip, { label: infoLabel, lines: infoLines })] }), _jsxs("a", { href: link.href, target: "_blank", rel: "noreferrer", className: "flex items-center gap-0.5 text-[10px] leading-none text-muted-foreground transition-colors hover:text-primary", children: [link.label, " ", _jsx(ExternalLink, { "aria-hidden": "true", className: "h-2.5 w-2.5" })] })] })] }), badge] }));
12
+ }
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * One icon action on a list row, carrying its own tooltip.
4
+ *
5
+ * Model rows and credential rows offer the same kind of trailing actions, so
6
+ * the button shape, its 28px hit area and the tooltip that names the action are
7
+ * defined once here instead of being re-built per row type.
8
+ */
9
+ export declare function AiRowAction({ label, disabled, onClick, children, }: {
10
+ /** Also the accessible name and the tooltip text. */
11
+ label: string;
12
+ disabled?: boolean;
13
+ onClick?: () => void;
14
+ children: ReactNode;
15
+ }): import("react").JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Tooltip, TooltipContent, TooltipTrigger } from '@undefineds.co/shared-ui';
3
+ /**
4
+ * One icon action on a list row, carrying its own tooltip.
5
+ *
6
+ * Model rows and credential rows offer the same kind of trailing actions, so
7
+ * the button shape, its 28px hit area and the tooltip that names the action are
8
+ * defined once here instead of being re-built per row type.
9
+ */
10
+ export function AiRowAction({ label, disabled, onClick, children, }) {
11
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", "aria-label": label, disabled: disabled, onClick: onClick, children: children }) }), _jsx(TooltipContent, { className: "text-xs", children: label })] }));
12
+ }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useLayoutEffect, useRef, useState } from 'react';
3
+ import { cn } from '@undefineds.co/shared-ui';
3
4
  import { GripVertical } from 'lucide-react';
4
5
  import { credentialDisplayLabel } from './credential-labels.js';
5
6
  export function AiSortableCredentialList({ credentials, disabled, onMove, children }) {
@@ -132,7 +133,7 @@ export function AiSortableCredentialList({ credentials, disabled, onMove, childr
132
133
  row.style.transform = `translateY(${shift}px)`;
133
134
  });
134
135
  };
135
- return (_jsxs("div", { ref: listRef, className: "space-y-2", children: [_jsx("span", { className: "sr-only", role: "status", children: announcement }), credentials.map((credential, index) => (_jsx("div", { "data-sortable-credential": credential.id, className: "rounded-lg", children: children(credential, onMove ? (_jsx("button", { type: "button", disabled: unavailable, "aria-label": `拖动排序 ${credentialDisplayLabel(credential)}`, title: credentials.length < 2 ? '至少添加两条连接后可拖动排序' : '拖动调整优先级,或使用方向键、Home / End 排序', className: "flex h-8 w-6 shrink-0 touch-none select-none items-center justify-center rounded text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:opacity-40 enabled:cursor-grab active:cursor-grabbing", onKeyDown: (event) => {
136
+ return (_jsxs("div", { ref: listRef, className: cn(credentials.length > 0 && 'rounded-xl border border-border/70'), children: [_jsx("span", { className: "sr-only", role: "status", children: announcement }), credentials.map((credential, index) => (_jsx("div", { "data-sortable-credential": credential.id, className: "border-b border-border/60 first-of-type:rounded-t-xl last-of-type:rounded-b-xl last:border-b-0", children: children(credential, onMove ? (_jsx("button", { type: "button", disabled: unavailable, "aria-label": `拖动排序 ${credentialDisplayLabel(credential)}`, title: credentials.length < 2 ? '至少添加两条连接后可拖动排序' : '拖动调整优先级,或使用方向键、Home / End 排序', className: "flex h-8 w-6 shrink-0 touch-none select-none items-center justify-center rounded text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:opacity-40 enabled:cursor-grab active:cursor-grabbing", onKeyDown: (event) => {
136
137
  if (event.key === 'Escape') {
137
138
  finish(false);
138
139
  return;
@@ -12,3 +12,10 @@ export { AI_CONNECTIONS_PROVIDERS } from './client/types';
12
12
  export type { AiConnectAttempt, AiConnectionBeginOptions, AiConnectionsClient, AiConnectionsCredential, AiConnectionsMode, AiConnectionsProvider, AiConnectStatus, AiGatewayModel, AiProviderAuthorizationMethod, AiProviderAuthorizationMethodId, AiProviderAuthorizationMethodsSummary, AiProviderConnectionSummary, AiProviderCredentialSummary, AiProviderOffering, AiProviderSummary, AiProviderSummaryStatus, AiQuotaSnapshot, AiQuotaWindow, CreateApiKeyCredentialInput, CreatedGatewayKey, CustomProviderModel, DiscoveredProviderModel, GatewayKeyRecord, ProviderModelDiscovery, TestProviderCredentialInput, UpdateProviderCredentialInput, } from './client/types';
13
13
  export { AI_CONNECTIONS_GENERIC_ERROR_MESSAGE, AiConnectionsRequestError, normalizeAiConnectionsErrorMessage, normalizeAiConnectionsThrownError, normalizeProxyUrl, redactProxyUrl, } from './client/normalize';
14
14
  export { createAiConnectionsClient, resolveAiConnectionsApiBase } from './client/request';
15
+ /**
16
+ * Storage-shape rules for credential rows. They are shared with the Pod adapter
17
+ * (`ui/src/extensions/XpodAiConnectionsPodStore.ts`) and with the collection
18
+ * layer that writes the same rows: one envelope format, one provider relation.
19
+ */
20
+ export { credentialProviderRelation, credentialRowKeyFor, credentialSecretEnvelope, customCredentialProviderRelation, decodeCredentialSecret, providerOfCredentialKey, providerResourceKey, providerResourceReference, } from './credential-storage';
21
+ export type { CredentialSecretEnvelopeInput, CredentialSecretReadInput, } from './credential-storage';
@@ -11,3 +11,9 @@
11
11
  export { AI_CONNECTIONS_PROVIDERS } from './client/types.js';
12
12
  export { AI_CONNECTIONS_GENERIC_ERROR_MESSAGE, AiConnectionsRequestError, normalizeAiConnectionsErrorMessage, normalizeAiConnectionsThrownError, normalizeProxyUrl, redactProxyUrl, } from './client/normalize.js';
13
13
  export { createAiConnectionsClient, resolveAiConnectionsApiBase } from './client/request.js';
14
+ /**
15
+ * Storage-shape rules for credential rows. They are shared with the Pod adapter
16
+ * (`ui/src/extensions/XpodAiConnectionsPodStore.ts`) and with the collection
17
+ * layer that writes the same rows: one envelope format, one provider relation.
18
+ */
19
+ export { credentialProviderRelation, credentialRowKeyFor, credentialSecretEnvelope, customCredentialProviderRelation, decodeCredentialSecret, providerOfCredentialKey, providerResourceKey, providerResourceReference, } from './credential-storage.js';