@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.
- package/config/components-ignore.json +5 -1
- package/config/notifications.json +68 -0
- package/config/xpod.base.json +9 -11
- package/dist/ai/service/AiCredentialSecret.d.ts +30 -0
- package/dist/ai/service/AiCredentialSecret.js +120 -0
- package/dist/ai/service/CredentialReaderImpl.d.ts +18 -0
- package/dist/ai/service/CredentialReaderImpl.js +81 -5
- package/dist/ai/service/CredentialReaderImpl.jsonld +42 -2
- package/dist/ai/service/EmbeddingModelPolicy.d.ts +79 -0
- package/dist/ai/service/EmbeddingModelPolicy.js +134 -0
- package/dist/ai/service/EmbeddingProviderError.js +6 -0
- package/dist/ai/service/EmbeddingServiceImpl.d.ts +13 -1
- package/dist/ai/service/EmbeddingServiceImpl.js +18 -5
- package/dist/ai/service/EmbeddingServiceImpl.jsonld +35 -0
- package/dist/ai/service/index.d.ts +2 -0
- package/dist/ai/service/index.js +2 -0
- package/dist/api/ai-gateway/connect/index.d.ts +20 -2
- package/dist/api/ai-gateway/connect/index.js +162 -16
- package/dist/api/ai-gateway/credentials/AiCredentialSecretDecoder.d.ts +12 -0
- package/dist/api/ai-gateway/credentials/AiCredentialSecretDecoder.js +30 -0
- package/dist/api/ai-gateway/errors.d.ts +1 -1
- package/dist/api/ai-gateway/models/GatewayEmbeddingModelCatalog.d.ts +11 -0
- package/dist/api/ai-gateway/models/GatewayEmbeddingModelCatalog.js +31 -0
- package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +7 -3
- package/dist/api/ai-gateway/models/ProviderCustomModelsService.d.ts +18 -0
- package/dist/api/ai-gateway/models/ProviderCustomModelsService.js +23 -0
- package/dist/api/ai-gateway/models/ProviderModelDiscoveryAdapters.d.ts +3 -1
- package/dist/api/ai-gateway/models/ProviderModelDiscoveryAdapters.js +2 -45
- package/dist/api/ai-gateway/models/ProviderModelSelectionService.d.ts +10 -0
- package/dist/api/ai-gateway/models/ProviderModelSelectionService.js +57 -20
- package/dist/api/ai-gateway/models/ProviderModelType.d.ts +28 -0
- package/dist/api/ai-gateway/models/ProviderModelType.js +34 -0
- package/dist/api/ai-gateway/models/ProviderModelsAdapter.d.ts +10 -0
- package/dist/api/ai-gateway/models/ProviderModelsAdapter.js +3 -4
- package/dist/api/ai-gateway/models/index.d.ts +1 -0
- package/dist/api/ai-gateway/models/index.js +3 -1
- package/dist/api/ai-gateway/providers/CustomRuntimeAdapter.d.ts +7 -0
- package/dist/api/ai-gateway/providers/CustomRuntimeAdapter.js +9 -2
- package/dist/api/ai-gateway/providers/ModelsDevCatalog.d.ts +7 -1
- package/dist/api/ai-gateway/providers/ModelsDevCatalog.js +4 -10
- package/dist/api/ai-gateway/providers/ProviderRegistry.d.ts +107 -0
- package/dist/api/ai-gateway/providers/ProviderRegistry.js +244 -21
- package/dist/api/ai-gateway/providers/ProviderRuntimeRegistry.d.ts +5 -0
- package/dist/api/ai-gateway/providers/ProviderRuntimeRegistry.js +5 -1
- package/dist/api/ai-gateway/quota/SubscriptionQuotaAdapters.d.ts +12 -4
- package/dist/api/ai-gateway/quota/SubscriptionQuotaAdapters.js +11 -11
- package/dist/api/ai-gateway/routing/ModelRouter.d.ts +14 -0
- package/dist/api/ai-gateway/routing/ModelRouter.js +43 -4
- package/dist/api/ai-gateway/service-access/AiConnectionsServiceAccess.d.ts +9 -1
- package/dist/api/ai-gateway/service-access/AiConnectionsServiceAccess.js +3 -27
- package/dist/api/chatkit/pod-store.d.ts +31 -0
- package/dist/api/chatkit/pod-store.js +132 -8
- package/dist/api/container/common.js +24 -4
- package/dist/api/container/routes.js +17 -2
- package/dist/api/container/types.d.ts +3 -1
- package/dist/api/handlers/AdminHandler.d.ts +13 -0
- package/dist/api/handlers/AdminHandler.js +47 -18
- package/dist/api/handlers/AiConfigHandler.d.ts +6 -0
- package/dist/api/handlers/AiConfigHandler.js +28 -0
- package/dist/api/handlers/AiGatewayManagementHandler.js +36 -0
- package/dist/api/runs/RdfRunContextRetriever.d.ts +14 -0
- package/dist/api/runs/RdfRunContextRetriever.js +41 -5
- package/dist/api/runtime.js +2 -1
- package/dist/cli/commands/secret.d.ts +1 -0
- package/dist/cli/commands/secret.js +25 -7
- package/dist/components/components.jsonld +3 -1
- package/dist/components/context.jsonld +53 -1
- package/dist/http/SubgraphSparqlHttpHandler.d.ts +59 -2
- package/dist/http/SubgraphSparqlHttpHandler.js +145 -13
- package/dist/http/SubgraphSparqlHttpHandler.jsonld +37 -0
- package/dist/identity/drizzle/EdgeNodeRepository.d.ts +0 -12
- package/dist/identity/drizzle/EdgeNodeRepository.js +5 -13
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -3
- package/dist/logging/log-file.d.ts +17 -0
- package/dist/logging/log-file.js +69 -0
- package/dist/main.js +2 -1
- package/dist/notifications/NotificationChannelSweeper.d.ts +50 -0
- package/dist/notifications/NotificationChannelSweeper.js +131 -0
- package/dist/notifications/NotificationChannelSweeper.jsonld +136 -0
- package/dist/notifications/ReclaimingWebSocket2023Storer.d.ts +25 -0
- package/dist/notifications/ReclaimingWebSocket2023Storer.js +56 -0
- package/dist/notifications/ReclaimingWebSocket2023Storer.jsonld +77 -0
- package/dist/notifications/index.d.ts +2 -0
- package/dist/notifications/index.js +2 -0
- package/dist/runtime/Proxy.d.ts +37 -0
- package/dist/runtime/Proxy.js +88 -16
- package/dist/runtime/bootstrap.js +2 -1
- package/dist/runtime/upgrade/BunNativeUpgradeRelay.d.ts +116 -0
- package/dist/runtime/upgrade/BunNativeUpgradeRelay.js +423 -0
- package/dist/runtime/upgrade/UpgradeHandshake.d.ts +63 -0
- package/dist/runtime/upgrade/UpgradeHandshake.js +199 -0
- package/dist/runtime/upgrade/WebSocketFrames.d.ts +67 -0
- package/dist/runtime/upgrade/WebSocketFrames.js +224 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.js +8 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiAuthorizationActions.d.ts +11 -2
- package/node_modules/@undefineds.co/ai-connections/dist/AiAuthorizationActions.js +27 -10
- package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.d.ts +7 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.js +13 -11
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectDialog.js +1 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsList.js +10 -6
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.d.ts +15 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +29 -3
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMainHeader.js +18 -3
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.d.ts +16 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.js +92 -20
- package/node_modules/@undefineds.co/ai-connections/dist/AiCopyButton.d.ts +35 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiCopyButton.js +45 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +71 -11
- package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialRow.js +9 -7
- package/node_modules/@undefineds.co/ai-connections/dist/AiEndpointList.d.ts +23 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiEndpointList.js +24 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.d.ts +13 -4
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +35 -38
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayModelsSection.d.ts +21 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayModelsSection.js +51 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiInfoTooltip.d.ts +15 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiInfoTooltip.js +16 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiModelCatalog.d.ts +175 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiModelCatalog.js +133 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiOfferingDetails.js +2 -2
- package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.js +13 -64
- package/node_modules/@undefineds.co/ai-connections/dist/AiProviderHeader.d.ts +21 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiProviderHeader.js +12 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiRowAction.d.ts +15 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiRowAction.js +12 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiSortableCredentialList.js +2 -1
- package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.d.ts +7 -0
- package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.js +6 -0
- package/node_modules/@undefineds.co/ai-connections/dist/authorization-methods.d.ts +36 -5
- package/node_modules/@undefineds.co/ai-connections/dist/authorization-methods.js +52 -9
- package/node_modules/@undefineds.co/ai-connections/dist/client/normalize.js +30 -3
- package/node_modules/@undefineds.co/ai-connections/dist/client/types.d.ts +21 -1
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.d.ts +1 -3
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.js +3 -7
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/index.cjs +9 -7
- package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.d.ts +46 -0
- package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.js +231 -0
- package/node_modules/@undefineds.co/ai-connections/dist/collections.d.ts +119 -0
- package/node_modules/@undefineds.co/ai-connections/dist/collections.js +173 -0
- package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +71 -5
- package/node_modules/@undefineds.co/ai-connections/dist/controller.js +285 -16
- package/node_modules/@undefineds.co/ai-connections/dist/credential-labels.d.ts +5 -0
- package/node_modules/@undefineds.co/ai-connections/dist/credential-labels.js +8 -3
- package/node_modules/@undefineds.co/ai-connections/dist/credential-storage.d.ts +59 -0
- package/node_modules/@undefineds.co/ai-connections/dist/credential-storage.js +131 -0
- package/node_modules/@undefineds.co/ai-connections/dist/endpoint-urls.d.ts +15 -0
- package/node_modules/@undefineds.co/ai-connections/dist/endpoint-urls.js +20 -0
- package/node_modules/@undefineds.co/ai-connections/dist/gateway-endpoints.d.ts +14 -0
- package/node_modules/@undefineds.co/ai-connections/dist/gateway-endpoints.js +20 -0
- package/node_modules/@undefineds.co/ai-connections/dist/index.d.ts +8 -0
- package/node_modules/@undefineds.co/ai-connections/dist/index.js +8 -0
- package/node_modules/@undefineds.co/ai-connections/dist/manifest.js +1 -1
- package/node_modules/@undefineds.co/ai-connections/dist/offering-endpoints.d.ts +14 -0
- package/node_modules/@undefineds.co/ai-connections/dist/offering-endpoints.js +17 -0
- package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.cjs +68 -1
- package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.d.ts +75 -1
- package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.js +119 -0
- package/node_modules/@undefineds.co/ai-connections/dist/provider-visuals.d.ts +5 -0
- package/node_modules/@undefineds.co/ai-connections/dist/provider-visuals.js +5 -0
- package/node_modules/@undefineds.co/ai-connections/dist/service-access.js +2 -27
- package/node_modules/@undefineds.co/ai-connections/package.json +1 -0
- package/node_modules/@undefineds.co/extension-sdk/dist/manifest.d.ts +1 -1
- package/node_modules/@undefineds.co/extension-sdk/dist/manifest.js +2 -0
- package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +150 -0
- package/node_modules/@undefineds.co/extension-sdk/package.json +2 -1
- package/node_modules/@undefineds.co/models/README.md +1 -1
- package/node_modules/@undefineds.co/models/dist/ai-config/index.d.ts +3 -3
- package/node_modules/@undefineds.co/models/dist/ai-gateway.schema.d.ts +5 -0
- package/node_modules/@undefineds.co/models/dist/ai-gateway.schema.js +9 -0
- package/node_modules/@undefineds.co/models/dist/ai-runtime.repository.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/approval.repository.d.ts +2 -2
- package/node_modules/@undefineds.co/models/dist/audit.presentation.d.ts +2 -2
- package/node_modules/@undefineds.co/models/dist/capture.repository.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/chat-project.repository.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/chat.repository.d.ts +2 -2
- package/node_modules/@undefineds.co/models/dist/control-request.repository.d.ts +2 -2
- package/node_modules/@undefineds.co/models/dist/conversation-share.repository.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/credential.schema.d.ts +12 -0
- package/node_modules/@undefineds.co/models/dist/credential.schema.js +15 -1
- package/node_modules/@undefineds.co/models/dist/discovery/index.d.ts +2 -2
- package/node_modules/@undefineds.co/models/dist/discovery/providers.json +248 -7
- package/node_modules/@undefineds.co/models/dist/discovery/types.d.ts +31 -0
- package/node_modules/@undefineds.co/models/dist/extension/index.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/favorite/index.d.ts +2 -2
- package/node_modules/@undefineds.co/models/dist/fixtures/contracts-chat-contact.d.ts +5 -5
- package/node_modules/@undefineds.co/models/dist/import/index.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/index.d.ts +73 -73
- package/node_modules/@undefineds.co/models/dist/index.js +1 -1
- package/node_modules/@undefineds.co/models/dist/input-request.repository.d.ts +2 -2
- package/node_modules/@undefineds.co/models/dist/knowledge/index.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/message.repository.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/namespaces.js +5 -0
- package/node_modules/@undefineds.co/models/dist/pod-storage-descriptor.d.ts +4815 -26
- package/node_modules/@undefineds.co/models/dist/pod-storage-descriptor.js +482 -132
- package/node_modules/@undefineds.co/models/dist/schema.d.ts +4 -0
- package/node_modules/@undefineds.co/models/dist/session/index.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/settings/index.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/sidecar/persistence-mapping.d.ts +5 -5
- package/node_modules/@undefineds.co/models/dist/thread.repository.d.ts +2 -2
- package/node_modules/@undefineds.co/models/dist/types/message-block.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/vocab/_namespaces.d.ts +1 -1
- package/node_modules/@undefineds.co/models/dist/vocab/index.d.ts +10 -10
- package/node_modules/@undefineds.co/models/dist/vocab/sidecar.vocab.d.ts +1 -0
- package/node_modules/@undefineds.co/models/dist/vocab/sidecar.vocab.js +1 -0
- package/node_modules/@undefineds.co/models/package.json +4 -2
- package/node_modules/@undefineds.co/pod-collections/README.md +71 -0
- package/node_modules/@undefineds.co/pod-collections/dist/diff.d.ts +85 -0
- package/node_modules/@undefineds.co/pod-collections/dist/diff.js +166 -0
- package/node_modules/@undefineds.co/pod-collections/dist/feed.d.ts +17 -0
- package/node_modules/@undefineds.co/pod-collections/dist/feed.js +27 -0
- package/node_modules/@undefineds.co/pod-collections/dist/index.cjs +1042 -0
- package/node_modules/@undefineds.co/pod-collections/dist/index.d.ts +37 -0
- package/node_modules/@undefineds.co/pod-collections/dist/index.js +89 -0
- package/node_modules/@undefineds.co/pod-collections/dist/layout.d.ts +75 -0
- package/node_modules/@undefineds.co/pod-collections/dist/layout.js +173 -0
- package/node_modules/@undefineds.co/pod-collections/dist/mapping.d.ts +78 -0
- package/node_modules/@undefineds.co/pod-collections/dist/mapping.js +287 -0
- package/node_modules/@undefineds.co/pod-collections/dist/mutations.d.ts +83 -0
- package/node_modules/@undefineds.co/pod-collections/dist/mutations.js +273 -0
- package/node_modules/@undefineds.co/pod-collections/dist/react.d.ts +21 -0
- package/node_modules/@undefineds.co/pod-collections/dist/react.js +22 -0
- package/node_modules/@undefineds.co/pod-collections/dist/read.d.ts +65 -0
- package/node_modules/@undefineds.co/pod-collections/dist/read.js +31 -0
- package/node_modules/@undefineds.co/pod-collections/dist/sync.d.ts +66 -0
- package/node_modules/@undefineds.co/pod-collections/dist/sync.js +260 -0
- package/node_modules/@undefineds.co/pod-collections/dist/types.d.ts +155 -0
- package/node_modules/@undefineds.co/pod-collections/dist/types.js +9 -0
- package/node_modules/@undefineds.co/pod-collections/package.json +42 -0
- package/package.json +15 -7
- package/static/app/assets/main.css +1 -1
- package/static/auth-callback/assets/AiConfigContext-DnQs1ZpW.js +1 -0
- package/static/auth-callback/assets/AiConfigPage-DOtvlxH0.js +1 -0
- package/static/auth-callback/assets/DocumentProcessingPanel-BS2voQgL.js +1 -0
- package/static/auth-callback/assets/IndexLifecyclePanel-Cvt0cJtN.js +1 -0
- package/static/auth-callback/assets/{IndexSubjectPanel-BbUqop4b.js → IndexSubjectPanel-Dhf01GqF.js} +1 -1
- package/static/auth-callback/assets/ModelAssignmentsPanel-BJutWaAF.js +1 -0
- package/static/auth-callback/assets/ModelsPage-BRS0sje3.js +11 -0
- package/static/auth-callback/assets/{NetworkPage-Vp2PrMxs.js → NetworkPage-B3qBfANA.js} +2 -2
- package/static/auth-callback/assets/{PaneListHeader-DY7baSFl.js → PaneListHeader-QQeJGTp4.js} +1 -1
- package/static/auth-callback/assets/SearchIndexingPanel-MyNUSEwT.js +1 -0
- package/static/auth-callback/assets/{StatusSubjectPanel-ArIS-TxT.js → StatusSubjectPanel-DQkEVsFs.js} +1 -1
- package/static/{settings/assets/StatusWorkspace-CoMFRXoV.js → auth-callback/assets/StatusWorkspace-Dkv63Uwk.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsPage-BDvT1NUz.js → SystemSettingsPage-TIsUfX5d.js} +1 -1
- package/static/{dashboard/assets/SystemSettingsSubjectPanel-WaE8mTQi.js → auth-callback/assets/SystemSettingsSubjectPanel-BJWEt2-7.js} +5 -5
- package/static/auth-callback/assets/XpodAiConnectionsPodStore-BJiXm-PF.js +3 -0
- package/static/auth-callback/assets/{activity-D1Dsfa_0.js → activity-5hNeNKnL.js} +1 -1
- package/static/auth-callback/assets/ai-connections-CryuHGKW.js +1 -0
- package/static/auth-callback/assets/{auth-callback-Cmf4hXVZ.js → auth-callback-CAFZ3R0B.js} +6 -6
- package/static/auth-callback/assets/auth-callback-rk080mvG.css +1 -0
- package/static/auth-callback/assets/{badge-CZsHq3Ih.js → badge-CAIPI0CB.js} +1 -1
- package/static/auth-callback/assets/collection-runtime-D9s7h9YA.js +1 -0
- package/static/auth-callback/assets/dialog-BjA109aX.js +1 -0
- package/static/auth-callback/assets/{download-DU9ntlHw.js → download-B3Up5W2G.js} +1 -1
- package/static/auth-callback/assets/{external-link-BfzaaZDx.js → external-link-BTXgSPzB.js} +1 -1
- package/static/auth-callback/assets/index-BppXlxcp.js +41 -0
- package/static/auth-callback/assets/index-CirW4CJf.js +1 -0
- package/static/auth-callback/assets/index-DlLYWOhw.js +4 -0
- package/static/auth-callback/assets/{index-browser-BNFqqC74.js → index-browser-DtU38ni3.js} +1 -1
- package/static/auth-callback/assets/{key-round-BwP3DBWo.js → key-round-CQCOrZ7x.js} +1 -1
- package/static/auth-callback/assets/layout-DB3pMyTl.js +1 -0
- package/static/auth-callback/assets/pod-collections-host-KnnGFZHL.js +8 -0
- package/static/auth-callback/assets/{rotate-ccw-VMwnmyab.js → rotate-ccw-C1zM2KtS.js} +1 -1
- package/static/auth-callback/assets/{skeleton-CoD55rBT.js → skeleton-Cqp7_M9-.js} +1 -1
- package/static/auth-callback/assets/{waypoints-DRo_1Ca7.js → waypoints-Bj6gAUSQ.js} +1 -1
- package/static/auth-callback/assets/{workspace-layout--mcXl3My.js → workspace-layout-DK8mDzrY.js} +1 -1
- package/static/auth-callback/auth-callback.html +2 -2
- package/static/dashboard/assets/AiConfigContext-BsE7MqX8.js +1 -0
- package/static/dashboard/assets/AiConfigPage-DvM-lRXa.js +1 -0
- package/static/dashboard/assets/DocumentProcessingPanel-BPfdlfuf.js +1 -0
- package/static/dashboard/assets/IndexLifecyclePanel--m0AGxvh.js +1 -0
- package/static/dashboard/assets/{IndexSubjectPanel-B2cXwl8X.js → IndexSubjectPanel-Bb7m1k3d.js} +1 -1
- package/static/dashboard/assets/ModelAssignmentsPanel-BLi_zbMa.js +1 -0
- package/static/dashboard/assets/ModelsPage-CxdpgnfR.js +11 -0
- package/static/dashboard/assets/{NetworkPage-DV2V0qWO.js → NetworkPage-BAIPw-D_.js} +2 -2
- package/static/dashboard/assets/{PaneListHeader-D0c1yCFH.js → PaneListHeader-BBCMei0r.js} +1 -1
- package/static/dashboard/assets/SearchIndexingPanel-Ck4Y-xjR.js +1 -0
- package/static/dashboard/assets/{StatusSubjectPanel-oPSECj7i.js → StatusSubjectPanel-D1KWfAXE.js} +1 -1
- package/static/dashboard/assets/{StatusWorkspace-PdovQPKa.js → StatusWorkspace-JbA5gzXx.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsPage-CtNDklkx.js → SystemSettingsPage-BbwQWsSR.js} +1 -1
- package/static/{settings/assets/SystemSettingsSubjectPanel-DExjDo_v.js → dashboard/assets/SystemSettingsSubjectPanel-N117nMt5.js} +5 -5
- package/static/dashboard/assets/XpodAiConnectionsPodStore-CdDOb4SV.js +18 -0
- package/static/dashboard/assets/{activity-B9ylSoyr.js → activity-DX1I96o_.js} +1 -1
- package/static/dashboard/assets/ai-connections-DwCRqOQa.js +1 -0
- package/static/dashboard/assets/{badge-BNbFbpIb.js → badge-k2vhtDqH.js} +1 -1
- package/static/dashboard/assets/collection-runtime-BFQ8teQJ.js +1 -0
- package/static/dashboard/assets/{dashboard-CD-FFVRD.js → dashboard-DWZ-fmCT.js} +4 -4
- package/static/dashboard/assets/dashboard-rk080mvG.css +1 -0
- package/static/dashboard/assets/dialog-1CCw-iHT.js +1 -0
- package/static/dashboard/assets/{download-nOP38LtW.js → download-C6-SNY1q.js} +1 -1
- package/static/dashboard/assets/{external-link-Bh2ewN9E.js → external-link-BgPcZ_j2.js} +1 -1
- package/static/dashboard/assets/index-6LcIUIYO.js +1 -0
- package/static/dashboard/assets/index-D7A8U6Pt.js +41 -0
- package/static/dashboard/assets/index-UD6QOE7H.js +4 -0
- package/static/dashboard/assets/{key-round-bGmguBLv.js → key-round-BOd7Jo0Q.js} +1 -1
- package/static/dashboard/assets/layout-DB3pMyTl.js +1 -0
- package/static/dashboard/assets/pod-collections-host-KnnGFZHL.js +8 -0
- package/static/dashboard/assets/{rotate-ccw-D2x__ODW.js → rotate-ccw-CCgJqsvx.js} +1 -1
- package/static/dashboard/assets/{skeleton-VOqft8WS.js → skeleton-Bc7abAn5.js} +1 -1
- package/static/dashboard/assets/{waypoints-fgRqm4oH.js → waypoints-s-opmJxx.js} +1 -1
- package/static/dashboard/assets/{workspace-layout-DV4dgeHX.js → workspace-layout-DqIQeTBV.js} +1 -1
- package/static/dashboard/dashboard.html +2 -2
- package/static/settings/assets/AiConfigContext-BB9KZySa.js +1 -0
- package/static/settings/assets/AiConfigPage-DGSXUkLs.js +1 -0
- package/static/settings/assets/DocumentProcessingPanel-Da2Re6Rg.js +1 -0
- package/static/settings/assets/IndexLifecyclePanel-BSZuIzgr.js +1 -0
- package/static/settings/assets/{IndexSubjectPanel-BgbI9c-a.js → IndexSubjectPanel-CFd0ay-r.js} +1 -1
- package/static/settings/assets/ModelAssignmentsPanel-CSAryvWJ.js +1 -0
- package/static/settings/assets/ModelsPage-BQwCwLHU.js +11 -0
- package/static/settings/assets/{NetworkPage-C8OA9Mtp.js → NetworkPage-BKymvsQc.js} +2 -2
- package/static/settings/assets/{PaneListHeader-COvlPJWj.js → PaneListHeader-Bjh3hRdQ.js} +1 -1
- package/static/settings/assets/SearchIndexingPanel-MjKL5QLU.js +1 -0
- package/static/settings/assets/{StatusSubjectPanel-BuLml7qn.js → StatusSubjectPanel-Bvl6K4mx.js} +1 -1
- package/static/settings/assets/StatusWorkspace-B0NzrP--.js +1 -0
- package/static/settings/assets/{SystemSettingsPage-DlzmaWV-.js → SystemSettingsPage-D2z6Z4FE.js} +1 -1
- package/static/{auth-callback/assets/SystemSettingsSubjectPanel-CDqRuC4t.js → settings/assets/SystemSettingsSubjectPanel-4sxma4X4.js} +5 -5
- package/static/settings/assets/XpodAiConnectionsPodStore-BkNQ8fZx.js +3 -0
- package/static/settings/assets/{activity-TLerXgMs.js → activity-CgJJGpjx.js} +1 -1
- package/static/settings/assets/ai-connections-CryuHGKW.js +1 -0
- package/static/settings/assets/{badge-Bcs8iWrH.js → badge-C4NypVyT.js} +1 -1
- package/static/settings/assets/collection-runtime-CTLCDxAi.js +1 -0
- package/static/settings/assets/dialog-Csq0vWUi.js +1 -0
- package/static/settings/assets/{download-Ct3QmHPt.js → download-DaLJJ-Wp.js} +1 -1
- package/static/settings/assets/{external-link-C8KiHbss.js → external-link--w_iW5Au.js} +1 -1
- package/static/settings/assets/index-B0mQa59b.js +4 -0
- package/static/settings/assets/index-BukHiu4y.js +1 -0
- package/static/settings/assets/index-COmVnZi2.js +41 -0
- package/static/settings/assets/{index-browser-CazX1pc2.js → index-browser-BGpvHwD0.js} +1 -1
- package/static/settings/assets/{key-round-B19YD1GA.js → key-round-Bk4YRjBu.js} +1 -1
- package/static/settings/assets/layout-DB3pMyTl.js +1 -0
- package/static/settings/assets/pod-collections-host-KnnGFZHL.js +8 -0
- package/static/settings/assets/{rotate-ccw-AQm2kMgU.js → rotate-ccw-TCabbqsr.js} +1 -1
- package/static/settings/assets/{settings-C-VBtt9r.js → settings-CJWpdbNC.js} +5 -5
- package/static/settings/assets/settings-rk080mvG.css +1 -0
- package/static/settings/assets/{skeleton-DyZ1OBYf.js → skeleton-JDHYVe7t.js} +1 -1
- package/static/settings/assets/{waypoints-CC3wq-SU.js → waypoints-DDvEl-MU.js} +1 -1
- package/static/settings/assets/{workspace-layout-BxXNKgf9.js → workspace-layout-DV5QU30U.js} +1 -1
- package/static/settings/settings.html +2 -2
- package/static/auth-callback/assets/AiConfigContext-DqWRSm7n.js +0 -1
- package/static/auth-callback/assets/AiConfigPage-C5YYyvzl.js +0 -1
- package/static/auth-callback/assets/DocumentProcessingPanel-DZCiacBz.js +0 -1
- package/static/auth-callback/assets/IndexLifecyclePanel-CCLeOljB.js +0 -1
- package/static/auth-callback/assets/ModelAssignmentsPanel-jUUTDKuh.js +0 -1
- package/static/auth-callback/assets/ModelsPage-B08zdtff.js +0 -10
- package/static/auth-callback/assets/SearchIndexingPanel-D4YRg1-8.js +0 -1
- package/static/auth-callback/assets/StatusWorkspace-Cp5LQ9Yx.js +0 -1
- package/static/auth-callback/assets/ai-connections-CHNTKemd.js +0 -1
- package/static/auth-callback/assets/auth-callback-DWMMiFjh.css +0 -1
- package/static/auth-callback/assets/index-DSNnLUbU.js +0 -4
- package/static/auth-callback/assets/index-FHhHoKgp.js +0 -41
- package/static/dashboard/assets/AiConfigContext-BpPDKSzI.js +0 -1
- package/static/dashboard/assets/AiConfigPage-wsqEZDIX.js +0 -1
- package/static/dashboard/assets/DocumentProcessingPanel-CDSy3juc.js +0 -1
- package/static/dashboard/assets/IndexLifecyclePanel-CVd6vQCX.js +0 -1
- package/static/dashboard/assets/ModelAssignmentsPanel-BeBZhnQd.js +0 -1
- package/static/dashboard/assets/ModelsPage-B6McSCJw.js +0 -25
- package/static/dashboard/assets/SearchIndexingPanel-CGs0OZ12.js +0 -1
- package/static/dashboard/assets/ai-connections-CHNTKemd.js +0 -1
- package/static/dashboard/assets/dashboard-DWMMiFjh.css +0 -1
- package/static/dashboard/assets/index-PgS-Bt7X.js +0 -41
- package/static/dashboard/assets/index-QN2QgEwr.js +0 -4
- package/static/settings/assets/AiConfigContext-zKQYdfai.js +0 -1
- package/static/settings/assets/AiConfigPage-Bs1EE3Ei.js +0 -1
- package/static/settings/assets/DocumentProcessingPanel-CSFfdOXS.js +0 -1
- package/static/settings/assets/IndexLifecyclePanel-BR9Su49B.js +0 -1
- package/static/settings/assets/ModelAssignmentsPanel-C0BE7nbk.js +0 -1
- package/static/settings/assets/ModelsPage-CU-YQ8_7.js +0 -10
- package/static/settings/assets/SearchIndexingPanel-Bdd46mIV.js +0 -1
- package/static/settings/assets/ai-connections-CHNTKemd.js +0 -1
- package/static/settings/assets/index-1Q2cRjZd.js +0 -41
- package/static/settings/assets/index-DBO_Vq0P.js +0 -4
- package/static/settings/assets/settings-DWMMiFjh.css +0 -1
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Button, TooltipProvider } from '@undefineds.co/shared-ui';
|
|
3
3
|
import { ExternalLink, Plus, Settings2 } from 'lucide-react';
|
|
4
4
|
import { offeringTitle } from './offering-label.js';
|
|
5
|
-
import { authorizationMethodsForOffering, isApiKeyMethod, isLocalMethod, isOAuthMethod, isOAuthMode, isPendingAttempt, } from './authorization-methods.js';
|
|
5
|
+
import { authorizationMethodsForOffering, connectEntryRank, isApiKeyMethod, isBrowserConnectMethod, isLocalMethod, isOAuthMethod, isOAuthMode, isPendingAttempt, } from './authorization-methods.js';
|
|
6
6
|
import { credentialDisplayLabel, maskAccountLabel } from './credential-labels.js';
|
|
7
7
|
import { AiQuotaCard } from './AiQuotaCard.js';
|
|
8
8
|
import { AiAuthorizationActions } from './AiAuthorizationActions.js';
|
|
@@ -25,7 +25,56 @@ export function AiCredentialPoolSection({ definition, product, status, accountLa
|
|
|
25
25
|
}];
|
|
26
26
|
const credentials = product?.credentials ?? [];
|
|
27
27
|
const offerings = product?.offerings.length ? product.offerings : fallbackOfferings;
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* The connect entries ARE the offerings' authorization methods, rendered as
|
|
30
|
+
* ONE ordered list.
|
|
31
|
+
*
|
|
32
|
+
* Two of them are actions of the page rather than of a single offering: the
|
|
33
|
+
* key entry (the dialog already lists every offering that accepts a key) and
|
|
34
|
+
* the console entry (every offering of a provider opens the same console, so
|
|
35
|
+
* four offerings declaring it stay one button). Everything else keeps its
|
|
36
|
+
* offering, because the click has to name which authorization it starts.
|
|
37
|
+
*
|
|
38
|
+
* The order comes from the method kind alone (`connectEntryRank`), so the
|
|
39
|
+
* toolbar reads the same on every provider - browser sign-in, device code,
|
|
40
|
+
* local login state, key - and no provider can reorder it. The console entry
|
|
41
|
+
* also yields to an entry that already carries its label: an offering's own
|
|
42
|
+
* browser login names that action, and two buttons with one name would be two
|
|
43
|
+
* claims about the same click.
|
|
44
|
+
*/
|
|
45
|
+
const offeringMethods = offerings.map((offering) => ({
|
|
46
|
+
offering,
|
|
47
|
+
methods: authorizationMethodsForOffering(offering),
|
|
48
|
+
}));
|
|
49
|
+
const apiOfferings = offeringMethods
|
|
50
|
+
.filter((entry) => entry.methods.some((method) => method.lifecycle === 'active' && isApiKeyMethod(method)))
|
|
51
|
+
.map((entry) => entry.offering);
|
|
52
|
+
const apiKeyMethod = offeringMethods
|
|
53
|
+
.flatMap((entry) => entry.methods)
|
|
54
|
+
.find((method) => isApiKeyMethod(method) && !isBrowserConnectMethod(method) && method.lifecycle === 'active' && method.label)
|
|
55
|
+
?? offeringMethods.flatMap((entry) => entry.methods)
|
|
56
|
+
.find((method) => isApiKeyMethod(method) && !isBrowserConnectMethod(method) && method.label);
|
|
57
|
+
const authorizationEntries = offeringMethods.flatMap((entry) => entry.methods
|
|
58
|
+
.filter((method) => method.label && (isOAuthMethod(method) || isLocalMethod(method)))
|
|
59
|
+
.map((method) => ({ offering: entry.offering, method })));
|
|
60
|
+
const consoleMethod = offeringMethods
|
|
61
|
+
.flatMap((entry) => entry.methods)
|
|
62
|
+
.find((method) => isBrowserConnectMethod(method) && method.lifecycle === 'active' && method.label)
|
|
63
|
+
?? offeringMethods.flatMap((entry) => entry.methods)
|
|
64
|
+
.find((method) => isBrowserConnectMethod(method) && method.label);
|
|
65
|
+
const consoleEntry = consoleMethod?.label
|
|
66
|
+
&& !authorizationEntries.some((entry) => entry.method.label === consoleMethod.label)
|
|
67
|
+
? consoleMethod
|
|
68
|
+
: undefined;
|
|
69
|
+
const connectEntries = [
|
|
70
|
+
...authorizationEntries.map((entry) => ({
|
|
71
|
+
key: `${entry.offering.id}:${entry.method.id}`,
|
|
72
|
+
rank: connectEntryRank(entry.method),
|
|
73
|
+
authorization: entry,
|
|
74
|
+
})),
|
|
75
|
+
...(consoleEntry ? [{ key: 'console-login', rank: connectEntryRank(consoleEntry), consoleMethod: consoleEntry }] : []),
|
|
76
|
+
...(apiKeyMethod ? [{ key: 'api-key', rank: connectEntryRank(apiKeyMethod), apiKeyMethod }] : []),
|
|
77
|
+
].sort((left, right) => left.rank - right.rank);
|
|
29
78
|
const authorizationPending = isPendingAttempt(attempt) && isOAuthMode(attempt?.mode);
|
|
30
79
|
const orderedCredentials = [...credentials].sort((a, b) => a.priority - b.priority);
|
|
31
80
|
const quotaBusy = credentials.some((credential) => quotas[credential.id]?.busy);
|
|
@@ -36,13 +85,18 @@ export function AiCredentialPoolSection({ definition, product, status, accountLa
|
|
|
36
85
|
onBeginBrowser,
|
|
37
86
|
onCreateLocalCredential,
|
|
38
87
|
});
|
|
39
|
-
return (_jsx(TooltipProvider, { children: _jsxs("section", { className: "space-y-3", "aria-label": "\u5F53\u524D\u8FDE\u63A5", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("h3", { className: "flex items-center gap-2 text-sm font-medium text-foreground/90", children: [_jsx(Settings2, { className: "h-4 w-4 text-primary" }), "\u5F53\u524D\u8FDE\u63A5"] }), _jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [credentials.length > 1 ? (_jsx(Button, { variant: "ghost", size: "sm", "aria-label": `刷新全部 ${definition.name}额度`, disabled: disabled || quotaBusy || !credentials.some((c) => c.enabled) || !onRefreshQuota, onClick: () => offerings.forEach((offering) => onRefreshQuota?.(offering)), children: quotaBusy ? '查询中' : '刷新全部额度' })) : null,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
88
|
+
return (_jsx(TooltipProvider, { children: _jsxs("section", { className: "space-y-3", "aria-label": "\u5F53\u524D\u8FDE\u63A5", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("h3", { className: "flex items-center gap-2 text-sm font-medium text-foreground/90", children: [_jsx(Settings2, { "aria-hidden": "true", className: "h-4 w-4 text-primary" }), "\u5F53\u524D\u8FDE\u63A5"] }), _jsxs("div", { "data-testid": "provider-connect-actions", className: "flex flex-wrap items-center justify-end gap-2", children: [credentials.length > 1 ? (_jsx(Button, { variant: "ghost", size: "sm", "aria-label": `刷新全部 ${definition.name}额度`, disabled: disabled || quotaBusy || !credentials.some((c) => c.enabled) || !onRefreshQuota, onClick: () => offerings.forEach((offering) => onRefreshQuota?.(offering)), children: quotaBusy ? '查询中' : '刷新全部额度' })) : null, connectEntries.map((item) => {
|
|
89
|
+
if ('authorization' in item) {
|
|
90
|
+
return (_jsx("fieldset", { className: "m-0 min-w-0 border-0 p-0", "aria-label": `${offeringTitle(item.authorization.offering)}快捷接入`, children: _jsx(AiAuthorizationActions, { methods: [item.authorization.method], offering: item.authorization.offering, busy: busy || dialog.saving, disabled: disabled || authorizationPending, onBeginOffering: onBeginOffering ? dialog.beginAuthorization : undefined, onCreateLocalCredential: onCreateLocalCredential ? dialog.beginLocal : undefined }) }, item.key));
|
|
91
|
+
}
|
|
92
|
+
if ('consoleMethod' in item) {
|
|
93
|
+
return (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", title: item.consoleMethod.lifecycle === 'unavailable' ? item.consoleMethod.reason : undefined, disabled: busy || dialog.saving || disabled || authorizationPending
|
|
94
|
+
|| item.consoleMethod.lifecycle === 'unavailable', onClick: dialog.beginBrowser, children: [_jsx(ExternalLink, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), item.consoleMethod.label] }, item.key));
|
|
95
|
+
}
|
|
96
|
+
return (_jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", "aria-label": "\u65B0\u5EFA API Key \u8FDE\u63A5", title: item.apiKeyMethod.lifecycle === 'unavailable' ? item.apiKeyMethod.reason : undefined, disabled: busy || dialog.saving || disabled || authorizationPending
|
|
97
|
+
|| item.apiKeyMethod.lifecycle === 'unavailable'
|
|
98
|
+
|| (!product && status === 'pending' && !attempt), onClick: dialog.beginApiKey, children: [_jsx(Plus, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), item.apiKeyMethod.label] }, item.key));
|
|
99
|
+
})] })] }), _jsxs("div", { "aria-label": "\u51ED\u636E\u5217\u8868", className: "space-y-2", children: [_jsx(AiSortableCredentialList, { credentials: orderedCredentials, disabled: busy || disabled, onMove: onReorderCredentials ? (from, to) => {
|
|
46
100
|
const offering = offerings.find((item) => item.id === orderedCredentials[from]?.offeringId);
|
|
47
101
|
if (offering)
|
|
48
102
|
onReorderCredentials(offering, orderedCredentials, from, to);
|
|
@@ -54,7 +108,13 @@ export function AiCredentialPoolSection({ definition, product, status, accountLa
|
|
|
54
108
|
: credential.label ? maskAccountLabel(credential.label) : credentialDisplayLabel(credential);
|
|
55
109
|
const state = quotas[credential.id];
|
|
56
110
|
const quotaState = state?.credentialId === credential.id ? state : undefined;
|
|
57
|
-
return _jsx(AiCredentialRow, { credential: credential, label: label, dragHandle: handle, kindLabel: credential.authMode === 'apiKey' ? 'API Key' : offeringTitle(offering), busy: busy, disabled: disabled,
|
|
58
|
-
|
|
111
|
+
return _jsx(AiCredentialRow, { credential: credential, label: label, dragHandle: handle, kindLabel: credential.authMode === 'apiKey' ? 'API Key' : offeringTitle(offering), busy: busy, disabled: disabled,
|
|
112
|
+
// A row action is a click, not a call site: the failure is already
|
|
113
|
+
// reported through `setProviderError`, so it must not also surface
|
|
114
|
+
// as an unhandled rejection from the button.
|
|
115
|
+
onToggle: onUpdateCredential
|
|
116
|
+
? (current, patch) => { void onUpdateCredential(current, patch).catch(() => undefined); }
|
|
117
|
+
: undefined, onTest: onTestCredential, onEdit: credential.authMode === 'apiKey' ? () => dialog.beginEdit(credential) : undefined, onDelete: () => credential.authMode === 'apiKey' ? onDeleteCredential?.(credential) : onDisconnect(credential), deleteAriaLabel: credential.authMode === 'apiKey' ? `删除 ${label}` : `${label} 移除`, quota: _jsx(AiQuotaCard, { compact: true, providerName: definition.name, offeringName: offeringTitle(offering), credentialLabel: credentialDisplayLabel(credential), multiple: credentials.length > 1, quota: quotaState?.quota, busy: quotaState?.busy ?? false, error: quotaState?.error, paused: !credential.enabled, disabled: disabled || !onRefreshQuota, onRefresh: () => onRefreshQuota?.(offering, credential) }) });
|
|
118
|
+
} }), !credentials.length ? (_jsx("p", { className: "flex items-center gap-2 py-6 text-sm text-muted-foreground", children: accountLabel ? maskAccountLabel(accountLabel) : '尚未添加连接' })) : null, credentials.length === 0 && (status === 'configured' || status === 'connected') ? (_jsx(Button, { variant: "ghost", size: "sm", disabled: busy || disabled, onClick: () => onDisconnect(), children: status === 'configured' ? '移除配置' : '断开连接' })) : null] }), _jsx(AiConnectDialog, { controller: dialog, definition: definition, offerings: offerings, apiOfferings: apiOfferings, credentials: credentials, status: status, attempt: attempt, attemptOfferingId: attemptOfferingId, apiKey: apiKey, baseUrl: baseUrl, busy: busy, disabled: disabled, error: error, onApiKeyChange: onApiKeyChange, onBaseUrlChange: onBaseUrlChange, onBeginApiKey: onBeginApiKey, onBeginBrowser: onBeginBrowser, onSaveApiKey: onSaveApiKey, onDisconnect: onDisconnect, onUpdateCredential: onUpdateCredential, onCreateApiKeyCredential: onCreateApiKeyCredential, onBeginOffering: onBeginOffering, onCancelConnect: onCancelConnect, onDismissError: onDismissError }), _jsxs("details", { className: "text-xs text-muted-foreground", children: [_jsx("summary", { className: "w-fit cursor-pointer", children: "\u63A5\u5165\u4FE1\u606F" }), _jsx("div", { className: "mt-2 space-y-3", children: offerings.map((offering) => _jsx(AiOfferingDetails, { offering: offering }, offering.id)) })] }), dialog.error && !dialog.open && dialog.error !== error?.message ? _jsx("p", { role: "alert", className: "text-sm text-destructive", children: dialog.error }) : null, error && (error.offeringId ? !dialog.open : !suppressError)
|
|
59
119
|
? _jsx("p", { role: "alert", className: "text-sm text-destructive", children: error.message }) : null] }) }));
|
|
60
120
|
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Tooltip, TooltipContent, TooltipTrigger, cn } from '@undefineds.co/shared-ui';
|
|
3
|
+
import { AiRowAction } from './AiRowAction.js';
|
|
3
4
|
import { Pause, Pencil, Play, PlugZap, Trash2 } from 'lucide-react';
|
|
4
5
|
import { credentialDisplayLabel, healthLabel, healthTone } from './credential-labels.js';
|
|
5
6
|
export function AiCredentialRow({ credential, label, busy, disabled, dragHandle, kindLabel, quota, onToggle, onTest, onEdit, onDelete, deleteAriaLabel, }) {
|
|
6
7
|
const actionDisabled = disabled || busy;
|
|
7
8
|
const tone = healthTone(credential.health);
|
|
8
9
|
const stateLabel = `${credential.enabled ? '已启用' : '已停用'} · ${healthLabel(credential.health)}`;
|
|
9
|
-
return (_jsxs("div", { "data-credential-state": credential.enabled ? 'enabled' : 'disabled', className: cn(
|
|
10
|
+
return (_jsxs("div", { "data-credential-state": credential.enabled ? 'enabled' : 'disabled', className: cn(
|
|
11
|
+
// The list owns the box and the dividers, so a row carries only its own
|
|
12
|
+
// tint: `rounded-[inherit]` keeps the first and last tint inside the
|
|
13
|
+
// container's corners.
|
|
14
|
+
'flex flex-wrap items-center gap-x-3 gap-y-2 rounded-[inherit] px-4 py-3 transition-colors', credential.enabled ? tone.row : 'bg-muted/40 opacity-70'), children: [dragHandle, _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { "aria-label": stateLabel, className: cn('h-2 w-2 shrink-0 rounded-full', credential.enabled ? tone.dot : 'bg-muted-foreground/40') }) }), _jsx(TooltipContent, { className: "text-xs", children: stateLabel })] }), _jsxs("div", { className: "min-w-0 flex-1 basis-24", children: [_jsx("p", { className: "truncate text-sm font-medium text-foreground", children: label }), kindLabel ? _jsx("p", { className: "text-xs text-muted-foreground", children: kindLabel }) : null, credential.maskedHint && !label.includes(credential.maskedHint)
|
|
10
15
|
? _jsx("p", { className: "mt-1 truncate font-mono text-xs text-muted-foreground", children: credential.maskedHint })
|
|
11
|
-
: null] }), _jsx("div", { className: "order-last w-full min-w-0 sm:order-none sm:w-auto sm:max-w-[45%]", role: "group", "aria-label": `${credentialDisplayLabel(credential)}额度`, children: quota }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [onTest ? (_jsx(
|
|
16
|
+
: null] }), _jsx("div", { className: "order-last w-full min-w-0 sm:order-none sm:w-auto sm:max-w-[45%]", role: "group", "aria-label": `${credentialDisplayLabel(credential)}额度`, children: quota }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [onTest ? (_jsx(AiRowAction, { label: `测试连接 ${label}`, disabled: actionDisabled, onClick: () => onTest(credential), children: _jsx(PlugZap, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) })) : null, onEdit ? (_jsx(AiRowAction, { label: `编辑 ${label}`, disabled: actionDisabled, onClick: onEdit, children: _jsx(Pencil, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) })) : null, onToggle ? (_jsx(AiRowAction, { label: `${credential.enabled ? '停用' : '启用'} ${label}`, disabled: actionDisabled, onClick: () => onToggle(credential, { enabled: !credential.enabled }), children: credential.enabled
|
|
12
17
|
? _jsx(Pause, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
|
|
13
|
-
: _jsx(Play, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) })) : null, onDelete ? (_jsx(
|
|
14
|
-
}
|
|
15
|
-
function RowAction({ label, disabled, onClick, children, }) {
|
|
16
|
-
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 })] }));
|
|
18
|
+
: _jsx(Play, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) })) : null, onDelete ? (_jsx(AiRowAction, { label: deleteAriaLabel ?? `删除 ${label}`, disabled: actionDisabled, onClick: onDelete, children: _jsx(Trash2, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) })) : null] })] }));
|
|
17
19
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type AiEndpointEntry } from './offering-endpoints';
|
|
2
|
+
/**
|
|
3
|
+
* Protocol/endpoint list shared by a provider offering's details and Xpod's own
|
|
4
|
+
* 接入信息: in both places a list of `protocol → base URL` pairs is what the user
|
|
5
|
+
* reads, so the rows (and their copy affordance) are one implementation.
|
|
6
|
+
*
|
|
7
|
+
* `copy` is opt-in because the provider pages read their upstream endpoints
|
|
8
|
+
* without offering them for pasting, while Xpod's own endpoints exist to be
|
|
9
|
+
* copied into a client.
|
|
10
|
+
*/
|
|
11
|
+
export declare function AiEndpointList({ endpoints, copy, display }: {
|
|
12
|
+
endpoints: readonly AiEndpointEntry[];
|
|
13
|
+
copy?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* `compact` shortens the address, `full` shows it in full.
|
|
16
|
+
*
|
|
17
|
+
* `protocol` drops the address and lists the accept-compatible protocols as
|
|
18
|
+
* chips. Xpod publishes three protocols from one gateway, so two of them even
|
|
19
|
+
* share an address: repeating it tells the reader less than naming the
|
|
20
|
+
* protocol they should pick, and the address stays one click away.
|
|
21
|
+
*/
|
|
22
|
+
display?: 'compact' | 'full' | 'protocol';
|
|
23
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { compatibleProtocolLabel, endpointDisplayValue, endpointProtocolLabel } from './offering-endpoints.js';
|
|
3
|
+
import { AiCopyButton } from './AiCopyButton.js';
|
|
4
|
+
/**
|
|
5
|
+
* Protocol/endpoint list shared by a provider offering's details and Xpod's own
|
|
6
|
+
* 接入信息: in both places a list of `protocol → base URL` pairs is what the user
|
|
7
|
+
* reads, so the rows (and their copy affordance) are one implementation.
|
|
8
|
+
*
|
|
9
|
+
* `copy` is opt-in because the provider pages read their upstream endpoints
|
|
10
|
+
* without offering them for pasting, while Xpod's own endpoints exist to be
|
|
11
|
+
* copied into a client.
|
|
12
|
+
*/
|
|
13
|
+
export function AiEndpointList({ endpoints, copy = false, display = 'compact' }) {
|
|
14
|
+
if (display === 'protocol') {
|
|
15
|
+
return (_jsx("ul", { className: "flex flex-wrap gap-2", children: endpoints.map((endpoint) => {
|
|
16
|
+
const label = compatibleProtocolLabel(endpoint.protocol);
|
|
17
|
+
return (_jsx("li", { children: copy ? (_jsx(AiCopyButton, { value: endpoint.baseUrl, label: `${label} 地址`, text: label, variant: "outline", className: "h-7 gap-1.5 px-2 text-[11px] font-normal", iconClassName: "h-3 w-3" })) : (_jsx("span", { className: "inline-flex h-7 items-center rounded-md border border-border px-2 text-[11px] text-muted-foreground", children: label })) }, endpoint.protocol));
|
|
18
|
+
}) }));
|
|
19
|
+
}
|
|
20
|
+
return (_jsx("dl", { className: "space-y-1 text-[11px] text-muted-foreground", children: endpoints.map((endpoint) => {
|
|
21
|
+
const label = endpointProtocolLabel(endpoint.protocol);
|
|
22
|
+
return (_jsxs("div", { className: "flex min-w-0 items-baseline gap-2", children: [_jsx("dt", { className: "shrink-0 text-foreground/70", children: label }), _jsx("dd", { className: "min-w-0 truncate font-mono", title: endpoint.baseUrl, children: display === 'full' ? endpoint.baseUrl : endpointDisplayValue(endpoint.baseUrl) }), copy ? (_jsx("dd", { className: "shrink-0", children: _jsx(AiCopyButton, { value: endpoint.baseUrl, label: `${label} 地址`, variant: "ghost", className: "h-6 gap-1 px-1.5 text-[11px]", iconClassName: "h-3 w-3" }) })) : null] }, `${endpoint.protocol}:${endpoint.baseUrl}`));
|
|
23
|
+
}) }));
|
|
24
|
+
}
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import type { AiConnectionsClient, AiGatewayModel } from './ai-connections-client';
|
|
2
2
|
import { type AiClientConfigurationBridge } from './AiClientConfigurationSection';
|
|
3
|
+
import { type GatewayModelSelection } from './AiGatewayModelsSection';
|
|
3
4
|
/**
|
|
4
5
|
* The Xpod provider page. From a coding client's point of view Xpod is just
|
|
5
|
-
* another provider, so this surface shares the provider pages'
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* another provider, so this surface shares the provider pages' components: the
|
|
7
|
+
* same header (`AiProviderHeader`), the same model list (`AiGatewayModelsSection`
|
|
8
|
+
* over `AiModelCatalog`), and the same endpoint list (`AiEndpointList`) that the
|
|
9
|
+
* provider pages use for their offerings' protocols.
|
|
8
10
|
*/
|
|
9
|
-
export declare function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewayModels, }: {
|
|
11
|
+
export declare function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewayModels, modelSelection, liveRevision, }: {
|
|
10
12
|
client: AiConnectionsClient;
|
|
11
13
|
clientConfigurationBridge?: AiClientConfigurationBridge;
|
|
12
14
|
gatewayModels?: AiGatewayModel[];
|
|
15
|
+
/** Publishes or withdraws models on the models list endpoint, from this page. */
|
|
16
|
+
modelSelection?: GatewayModelSelection;
|
|
17
|
+
/**
|
|
18
|
+
* Advances when the credentials table changed outside this page (another
|
|
19
|
+
* client, a script); the Key list lives in that table and re-reads with it.
|
|
20
|
+
*/
|
|
21
|
+
liveRevision?: number;
|
|
13
22
|
}): import("react").JSX.Element;
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { Check,
|
|
3
|
+
import { Badge, Button, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, TooltipProvider, controlFocusClass, dismissToast, toast, } from '@undefineds.co/shared-ui';
|
|
4
|
+
import { Check, Loader2, Plus, Settings2 } from 'lucide-react';
|
|
5
5
|
import { AI_CONNECTIONS_CLIENTS, AI_CLIENT_LABELS, manualConfigurationText, } from './AiClientConfigurationSection.js';
|
|
6
|
+
import { AiCopyButton } from './AiCopyButton.js';
|
|
7
|
+
import { AiEndpointList } from './AiEndpointList.js';
|
|
8
|
+
import { AiProviderHeader } from './AiProviderHeader.js';
|
|
9
|
+
import { xpodProtocolEndpoints } from './gateway-endpoints.js';
|
|
10
|
+
import { XPOD_AVATAR } from './provider-visuals.js';
|
|
6
11
|
import { AiGatewayKeyRow } from './AiGatewayKeyRow.js';
|
|
12
|
+
import { AiGatewayModelsSection } from './AiGatewayModelsSection.js';
|
|
7
13
|
const DEFAULT_KEY_NAME = '我的 API Key';
|
|
8
14
|
const CREATED_NOTIFICATION = 'API Key 已创建,请复制或应用到客户端。';
|
|
9
15
|
const UNPERSISTED_COPY_MESSAGE = '这个 API Key 只在创建时可见:请销毁它,然后重新创建并立即复制或应用。';
|
|
10
16
|
const UNPERSISTED_APPLY_MESSAGE = '这个 API Key 只在创建时可见:请销毁它,然后重新创建并直接应用到客户端。';
|
|
17
|
+
/** Header tooltip: what Xpod is, then how the Key itself is protected. */
|
|
18
|
+
const XPOD_DESCRIPTION = '把已接入的 Provider 模型统一发布给编码客户端,兼容 OpenAI 与 Anthropic 协议。';
|
|
19
|
+
const XPOD_CREDENTIAL_NOTE = 'API Key 保存在当前 Pod,由 Pod 权限保护;Xpod 不保存明文。';
|
|
11
20
|
const SELECT_CLASS = [
|
|
12
21
|
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm transition-[border-color]',
|
|
13
22
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
@@ -15,11 +24,12 @@ const SELECT_CLASS = [
|
|
|
15
24
|
].join(' ');
|
|
16
25
|
/**
|
|
17
26
|
* The Xpod provider page. From a coding client's point of view Xpod is just
|
|
18
|
-
* another provider, so this surface shares the provider pages'
|
|
19
|
-
*
|
|
20
|
-
*
|
|
27
|
+
* another provider, so this surface shares the provider pages' components: the
|
|
28
|
+
* same header (`AiProviderHeader`), the same model list (`AiGatewayModelsSection`
|
|
29
|
+
* over `AiModelCatalog`), and the same endpoint list (`AiEndpointList`) that the
|
|
30
|
+
* provider pages use for their offerings' protocols.
|
|
21
31
|
*/
|
|
22
|
-
export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewayModels, }) {
|
|
32
|
+
export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewayModels, modelSelection, liveRevision = 0, }) {
|
|
23
33
|
const [keys, setKeys] = useState([]);
|
|
24
34
|
const [loading, setLoading] = useState(true);
|
|
25
35
|
const [showCreate, setShowCreate] = useState(false);
|
|
@@ -32,13 +42,11 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewa
|
|
|
32
42
|
const [issuedClient, setIssuedClient] = useState();
|
|
33
43
|
const [appliedClient, setAppliedClient] = useState();
|
|
34
44
|
const [applying, setApplying] = useState(false);
|
|
35
|
-
const [copied, setCopied] = useState();
|
|
36
45
|
const [busyKeyId, setBusyKeyId] = useState();
|
|
37
46
|
const [error, setError] = useState();
|
|
38
47
|
const plaintexts = useRef(new Map());
|
|
39
48
|
const operation = useRef(false);
|
|
40
49
|
const notification = useRef(undefined);
|
|
41
|
-
const copyTimer = useRef(undefined);
|
|
42
50
|
const notify = useCallback((options) => {
|
|
43
51
|
if (notification.current)
|
|
44
52
|
dismissToast(notification.current);
|
|
@@ -69,16 +77,30 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewa
|
|
|
69
77
|
});
|
|
70
78
|
return () => {
|
|
71
79
|
active = false;
|
|
72
|
-
clearTimeout(copyTimer.current);
|
|
73
80
|
if (notification.current)
|
|
74
81
|
dismissToast(notification.current);
|
|
75
82
|
};
|
|
76
83
|
}, [client, notifyError]);
|
|
84
|
+
// A change made elsewhere only needs new rows: it must not drop the wrapper
|
|
85
|
+
// copies this session is still showing, nor flash the loading state.
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (liveRevision === 0)
|
|
88
|
+
return;
|
|
89
|
+
let active = true;
|
|
90
|
+
void client.listGatewayKeys()
|
|
91
|
+
.then((records) => {
|
|
92
|
+
if (active)
|
|
93
|
+
setKeys(records.filter((record) => !record.revokedAt));
|
|
94
|
+
})
|
|
95
|
+
.catch(() => undefined);
|
|
96
|
+
return () => {
|
|
97
|
+
active = false;
|
|
98
|
+
};
|
|
99
|
+
}, [client, liveRevision]);
|
|
77
100
|
const openCreate = () => {
|
|
78
101
|
setError(undefined);
|
|
79
102
|
setName(DEFAULT_KEY_NAME);
|
|
80
103
|
setPurpose('');
|
|
81
|
-
setCopied(undefined);
|
|
82
104
|
// A key issued earlier in this session keeps its wrapper in the cache, so
|
|
83
105
|
// the flow resumes there instead of pretending the key was never created.
|
|
84
106
|
setShowCreate(true);
|
|
@@ -87,7 +109,6 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewa
|
|
|
87
109
|
setIssued(undefined);
|
|
88
110
|
setIssuedClient(undefined);
|
|
89
111
|
setAppliedClient(undefined);
|
|
90
|
-
setCopied(undefined);
|
|
91
112
|
setError(undefined);
|
|
92
113
|
setName(DEFAULT_KEY_NAME);
|
|
93
114
|
setPurpose('');
|
|
@@ -116,7 +137,6 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewa
|
|
|
116
137
|
setIssued(created.record);
|
|
117
138
|
setIssuedClient(purpose);
|
|
118
139
|
setAppliedClient(undefined);
|
|
119
|
-
setCopied(undefined);
|
|
120
140
|
notify({ variant: 'success', description: CREATED_NOTIFICATION });
|
|
121
141
|
}
|
|
122
142
|
catch (cause) {
|
|
@@ -132,24 +152,6 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewa
|
|
|
132
152
|
throw new Error(message);
|
|
133
153
|
return plaintext;
|
|
134
154
|
};
|
|
135
|
-
const copy = async (record, target) => {
|
|
136
|
-
setError(undefined);
|
|
137
|
-
try {
|
|
138
|
-
const plaintext = sessionPlaintext(record, UNPERSISTED_COPY_MESSAGE);
|
|
139
|
-
if (!navigator.clipboard?.writeText) {
|
|
140
|
-
throw new Error('当前浏览器无法访问剪贴板,请允许剪贴板访问后重试。');
|
|
141
|
-
}
|
|
142
|
-
await navigator.clipboard.writeText(target === 'key'
|
|
143
|
-
? plaintext
|
|
144
|
-
: manualConfigurationText(target, client.apiBase, plaintext));
|
|
145
|
-
setCopied(target);
|
|
146
|
-
clearTimeout(copyTimer.current);
|
|
147
|
-
copyTimer.current = setTimeout(() => setCopied(undefined), 2000);
|
|
148
|
-
}
|
|
149
|
-
catch (cause) {
|
|
150
|
-
setError(errorMessage(cause));
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
155
|
const apply = async () => {
|
|
154
156
|
if (!issued || !issuedClient || !clientConfigurationBridge)
|
|
155
157
|
return;
|
|
@@ -212,7 +214,6 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewa
|
|
|
212
214
|
setIssued(undefined);
|
|
213
215
|
setIssuedClient(undefined);
|
|
214
216
|
setAppliedClient(undefined);
|
|
215
|
-
setCopied(undefined);
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
catch (cause) {
|
|
@@ -223,13 +224,9 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewa
|
|
|
223
224
|
setBusyKeyId(undefined);
|
|
224
225
|
}
|
|
225
226
|
};
|
|
226
|
-
return (_jsxs("section", { className: "space-y-8
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
: _jsx(Copy, { "aria-hidden": "true", className: "mr-1.5 h-3.5 w-3.5" }), copied === 'key' ? '已复制' : '复制 API Key'] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: applying, "aria-label": `复制 ${AI_CLIENT_LABELS[issuedClient]} 配置`, onClick: () => void copy(issued, issuedClient), children: [copied === issuedClient
|
|
230
|
-
? _jsx(Check, { "aria-hidden": "true", className: "mr-1.5 h-3.5 w-3.5 text-emerald-600" })
|
|
231
|
-
: _jsx(Copy, { "aria-hidden": "true", className: "mr-1.5 h-3.5 w-3.5" }), copied === issuedClient ? '已复制' : `复制 ${AI_CLIENT_LABELS[issuedClient]} 配置`] }), clientConfigurationBridge ? (appliedClient ? (_jsxs("span", { role: "status", className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Check, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), "\u5DF2\u5E94\u7528\u5230 ", AI_CLIENT_LABELS[appliedClient]] })) : (_jsxs(Button, { type: "button", size: "sm", disabled: applying, "aria-label": `应用到 ${AI_CLIENT_LABELS[issuedClient]}`, onClick: () => void apply(), children: [applying ? _jsx(Loader2, { "aria-hidden": "true", className: "mr-2 h-4 w-4 animate-spin" }) : null, applying ? '正在应用…' : `应用到 ${AI_CLIENT_LABELS[issuedClient]}`] }))) : null] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", disabled: applying, onClick: beginAnother, children: "\u518D\u5EFA\u4E00\u4E2A" }), _jsx(Button, { type: "button", variant: "outline", onClick: () => setShowCreate(false), children: "\u5B8C\u6210" })] })] })) : (_jsxs("form", { className: "space-y-5", onSubmit: (event) => { event.preventDefault(); if (!creating)
|
|
232
|
-
void create(); }, children: [_jsxs("label", { className: "block space-y-2", children: [_jsx("span", { className: "text-sm font-medium", children: "\u540D\u79F0" }), _jsx(Input, { autoFocus: true, "aria-label": "API Key \u540D\u79F0", value: name, disabled: creating, onChange: (event) => setName(event.target.value) })] }), _jsxs("label", { className: "block space-y-2", children: [_jsx("span", { className: "text-sm font-medium", children: "\u7528\u9014" }), _jsxs("select", { "aria-label": "API Key \u7528\u9014", className: SELECT_CLASS, value: purpose, disabled: creating, onChange: (event) => setPurpose(event.target.value), children: [_jsx("option", { value: "", disabled: true, children: "\u9009\u62E9\u5BA2\u6237\u7AEF\u5E94\u7528" }), AI_CONNECTIONS_CLIENTS.map((clientId) => (_jsx("option", { value: clientId, children: AI_CLIENT_LABELS[clientId] }, clientId)))] })] }), _jsx("p", { className: "text-xs text-muted-foreground", children: "\u7528\u9014\u5728\u521B\u5EFA\u65F6\u786E\u5B9A\uFF0CKey \u4F1A\u7ED1\u5B9A\u5230\u8BE5\u5BA2\u6237\u7AEF\u5E94\u7528\u3002" }), error ? _jsx("p", { role: "alert", className: "text-sm text-destructive", children: error }) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", disabled: creating, onClick: () => setShowCreate(false), children: "\u53D6\u6D88" }), _jsxs(Button, { type: "submit", "aria-label": "\u521B\u5EFA API Key", disabled: creating || !name.trim() || !purpose, children: [creating ? _jsx(Loader2, { "aria-hidden": "true", className: "mr-2 h-4 w-4 animate-spin" }) : null, creating ? '正在创建…' : '创建 API Key'] })] })] }))] }) })] }));
|
|
227
|
+
return (_jsx(TooltipProvider, { children: _jsxs("section", { className: "space-y-8", "aria-label": "API Keys", children: [_jsx(AiProviderHeader, { name: "Xpod", mark: "XP", avatar: XPOD_AVATAR, infoLabel: "Xpod \u8BF4\u660E", infoLines: [XPOD_DESCRIPTION, XPOD_CREDENTIAL_NOTE], link: { href: client.apiBase, label: '访问 Xpod' }, badge: (_jsx(Badge, { variant: keys.length > 0 ? 'default' : 'secondary', children: keys.length > 0 ? `已签发 ${keys.length} 个` : '未配置' })) }), _jsxs("section", { className: "space-y-3", "aria-label": "\u5F53\u524D\u8FDE\u63A5", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("h3", { className: "flex items-center gap-2 text-sm font-medium text-foreground/90", children: [_jsx(Settings2, { "aria-hidden": "true", className: "h-4 w-4 text-primary" }), "\u5F53\u524D\u8FDE\u63A5"] }), _jsx("div", { className: "flex flex-wrap items-center justify-end gap-2", children: _jsxs(Button, { variant: "outline", size: "sm", className: "h-8 gap-1.5 text-xs", "aria-label": "\u65B0\u5EFA API Key", disabled: creating || loading, onClick: openCreate, children: [_jsx(Plus, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), "API Key"] }) })] }), loading ? (_jsxs("div", { className: "flex items-center gap-2 py-6 text-sm text-muted-foreground", children: [_jsx(Loader2, { "aria-hidden": "true", className: "h-4 w-4 animate-spin" }), "\u6B63\u5728\u8BFB\u53D6 API Key"] })) : keys.length === 0 ? (_jsx("p", { className: "py-2 text-xs text-muted-foreground", children: "\u5C1A\u672A\u7B7E\u53D1 API Key" })) : (_jsx("ul", { "aria-label": "API Key \u5217\u8868", className: "rounded-xl border border-border/70", children: keys.map((record) => (_jsx(AiGatewayKeyRow, { record: record, busy: Boolean(busyKeyId), onDestroy: () => void destroy(record) }, record.id))) })), _jsxs("details", { className: "text-xs text-muted-foreground", children: [_jsx("summary", { className: "w-fit cursor-pointer", children: "\u63A5\u5165\u4FE1\u606F" }), _jsx("div", { className: "mt-2 space-y-3", children: _jsxs("section", { className: "space-y-2", "aria-labelledby": "xpod-access-info", children: [_jsx("h4", { id: "xpod-access-info", className: "text-sm font-medium text-foreground", children: "Xpod \u63A5\u5165\u4FE1\u606F" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "API Key \u662F\u7B7E\u53D1\u7ED9\u5BA2\u6237\u7AEF\u5E94\u7528\u7684 CSS \u5BA2\u6237\u7AEF\u51ED\u636E\uFF1BPod \u53EA\u8BB0\u5F55\u5B83\u7ED1\u5B9A\u5230\u54EA\u4E2A\u5BA2\u6237\u7AEF\uFF0C\u4E0D\u4FDD\u5B58 Key \u660E\u6587\u3002" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "\u540C\u4E00\u4E2A API Key \u652F\u6301\u4EE5\u4E0B\u517C\u5BB9\u534F\u8BAE\uFF0C\u6309\u5BA2\u6237\u7AEF\u9009\u62E9\u5E76\u590D\u5236\u5BF9\u5E94\u5730\u5740\uFF1A" }), _jsx(AiEndpointList, { endpoints: xpodProtocolEndpoints(client.apiBase), copy: true, display: "protocol" })] }) })] })] }), _jsx(AiGatewayModelsSection, { models: gatewayModels, selection: modelSelection }), _jsx(Dialog, { open: showCreate, onOpenChange: (open) => { if (!open && (creating || applying))
|
|
228
|
+
return; setShowCreate(open); }, children: _jsxs(DialogContent, { className: "sm:max-w-md", "aria-describedby": undefined, children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: issued ? 'API Key 已签发' : '新建 API Key' }) }), issued && issuedClient ? (_jsxs("div", { className: "space-y-5", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs("p", { className: "text-sm", children: ["\u5DF2\u7B7E\u53D1\u300C", issued.name || '未命名 API Key', "\u300D\uFF0C\u7528\u9014\uFF1A", AI_CLIENT_LABELS[issuedClient], "\u3002"] }), _jsx("p", { className: "text-xs text-muted-foreground", children: "Xpod \u4E0D\u4FDD\u5B58 Key \u660E\u6587\uFF0C\u8BF7\u7ACB\u5373\u590D\u5236\u6216\u76F4\u63A5\u5E94\u7528\u5230\u5BA2\u6237\u7AEF\uFF1B\u79BB\u5F00\u672C\u6B21\u4F1A\u8BDD\u540E\u65E0\u6CD5\u518D\u6B21\u83B7\u53D6\u3002" })] }), issued.maskedHint ? (_jsx("code", { className: "block truncate rounded-md border border-border/60 bg-muted/30 px-3 py-2 font-mono text-xs text-muted-foreground", children: issued.maskedHint })) : null, !clientConfigurationBridge ? (_jsx("p", { className: "text-xs text-muted-foreground", children: "\u5F53\u524D Web \u73AF\u5883\u65E0\u6CD5\u81EA\u52A8\u5199\u5165\u5BA2\u6237\u7AEF\u914D\u7F6E\uFF0C\u8BF7\u590D\u5236\u540E\u624B\u52A8\u7C98\u8D34\uFF1B\u81EA\u52A8\u5E94\u7528\u9700\u8981\u672C\u673A\u8FDE\u63A5\u3002" })) : null, error ? _jsx("p", { role: "alert", className: "text-sm text-destructive", children: error }) : null, _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(AiCopyButton, { value: () => sessionPlaintext(issued, UNPERSISTED_COPY_MESSAGE), label: "API Key", text: "\u590D\u5236 API Key", disabled: applying, iconClassName: "mr-1.5 h-3.5 w-3.5", copiedIconClassName: "mr-1.5 h-3.5 w-3.5 text-emerald-600", onError: (cause) => setError(errorMessage(cause)) }), _jsx(AiCopyButton, { value: () => manualConfigurationText(issuedClient, client.apiBase, sessionPlaintext(issued, UNPERSISTED_COPY_MESSAGE)), label: `${AI_CLIENT_LABELS[issuedClient]} 配置`, text: `复制 ${AI_CLIENT_LABELS[issuedClient]} 配置`, disabled: applying, iconClassName: "mr-1.5 h-3.5 w-3.5", copiedIconClassName: "mr-1.5 h-3.5 w-3.5 text-emerald-600", onError: (cause) => setError(errorMessage(cause)) }), clientConfigurationBridge ? (appliedClient ? (_jsxs("span", { role: "status", className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Check, { "aria-hidden": "true", className: "h-3.5 w-3.5" }), "\u5DF2\u5E94\u7528\u5230 ", AI_CLIENT_LABELS[appliedClient]] })) : (_jsxs(Button, { type: "button", size: "sm", disabled: applying, "aria-label": `应用到 ${AI_CLIENT_LABELS[issuedClient]}`, onClick: () => void apply(), children: [applying ? _jsx(Loader2, { "aria-hidden": "true", className: "mr-2 h-4 w-4 animate-spin" }) : null, applying ? '正在应用…' : `应用到 ${AI_CLIENT_LABELS[issuedClient]}`] }))) : null] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", disabled: applying, onClick: beginAnother, children: "\u518D\u5EFA\u4E00\u4E2A" }), _jsx(Button, { type: "button", variant: "outline", onClick: () => setShowCreate(false), children: "\u5B8C\u6210" })] })] })) : (_jsxs("form", { className: "space-y-5", onSubmit: (event) => { event.preventDefault(); if (!creating)
|
|
229
|
+
void create(); }, children: [_jsxs("label", { className: "block space-y-2", children: [_jsx("span", { className: "text-sm font-medium", children: "\u540D\u79F0" }), _jsx(Input, { autoFocus: true, "aria-label": "API Key \u540D\u79F0", value: name, disabled: creating, onChange: (event) => setName(event.target.value) })] }), _jsxs("label", { className: "block space-y-2", children: [_jsx("span", { className: "text-sm font-medium", children: "\u7528\u9014" }), _jsxs("select", { "aria-label": "API Key \u7528\u9014", className: SELECT_CLASS, value: purpose, disabled: creating, onChange: (event) => setPurpose(event.target.value), children: [_jsx("option", { value: "", disabled: true, children: "\u9009\u62E9\u5BA2\u6237\u7AEF\u5E94\u7528" }), AI_CONNECTIONS_CLIENTS.map((clientId) => (_jsx("option", { value: clientId, children: AI_CLIENT_LABELS[clientId] }, clientId)))] })] }), _jsx("p", { className: "text-xs text-muted-foreground", children: "\u7528\u9014\u5728\u521B\u5EFA\u65F6\u786E\u5B9A\uFF0CKey \u4F1A\u7ED1\u5B9A\u5230\u8BE5\u5BA2\u6237\u7AEF\u5E94\u7528\u3002" }), error ? _jsx("p", { role: "alert", className: "text-sm text-destructive", children: error }) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", disabled: creating, onClick: () => setShowCreate(false), children: "\u53D6\u6D88" }), _jsxs(Button, { type: "submit", "aria-label": "\u521B\u5EFA API Key", disabled: creating || !name.trim() || !purpose, children: [creating ? _jsx(Loader2, { "aria-hidden": "true", className: "mr-2 h-4 w-4 animate-spin" }) : null, creating ? '正在创建…' : '创建 API Key'] })] })] }))] }) })] }) }));
|
|
233
230
|
}
|
|
234
231
|
function errorMessage(error) {
|
|
235
232
|
const message = error instanceof Error ? error.message.trim() : '';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AiGatewayModel } from './ai-connections-client';
|
|
2
|
+
/**
|
|
3
|
+
* What the gateway list shares with the provider pages.
|
|
4
|
+
*
|
|
5
|
+
* A published model is a joined one, so switching here writes the same
|
|
6
|
+
* selection the provider pages write rather than a second flag to keep in sync.
|
|
7
|
+
*/
|
|
8
|
+
export interface GatewayModelSelection {
|
|
9
|
+
isSelected(model: AiGatewayModel): boolean;
|
|
10
|
+
toggle(model: AiGatewayModel): void;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The catalog the Gateway publishes to clients (`/v1/models`), switched with the
|
|
15
|
+
* same row the provider pages use: 停用 withdraws a model from the models list
|
|
16
|
+
* endpoint, 启用 publishes it again.
|
|
17
|
+
*/
|
|
18
|
+
export declare function AiGatewayModelsSection({ models, selection }: {
|
|
19
|
+
models?: AiGatewayModel[];
|
|
20
|
+
selection?: GatewayModelSelection;
|
|
21
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { Badge } from '@undefineds.co/shared-ui';
|
|
4
|
+
import { Box } from 'lucide-react';
|
|
5
|
+
import { AiModelEmptyPanel, AiModelSearchInput, AiModelRow, modelIconTokens, withCatalogModelId, } from './AiModelCatalog.js';
|
|
6
|
+
/**
|
|
7
|
+
* The catalog the Gateway publishes to clients (`/v1/models`), switched with the
|
|
8
|
+
* same row the provider pages use: 停用 withdraws a model from the models list
|
|
9
|
+
* endpoint, 启用 publishes it again.
|
|
10
|
+
*/
|
|
11
|
+
export function AiGatewayModelsSection({ models, selection }) {
|
|
12
|
+
const [search, setSearch] = useState('');
|
|
13
|
+
const catalog = useMemo(() => aggregateGatewayModels(models ?? []), [models]);
|
|
14
|
+
const query = search.trim().toLocaleLowerCase();
|
|
15
|
+
const visibleModels = query
|
|
16
|
+
? catalog.filter((model) => model.searchText.includes(query))
|
|
17
|
+
: catalog;
|
|
18
|
+
const unavailableCount = catalog.filter((model) => model.availability === 'unavailable').length;
|
|
19
|
+
const publishedCount = selection
|
|
20
|
+
? catalog.filter((model) => selection.isSelected(model)).length
|
|
21
|
+
: catalog.length;
|
|
22
|
+
return (_jsxs("section", { className: "space-y-8", "aria-label": "\u53EF\u7528\u6A21\u578B", children: [_jsxs("div", { "data-testid": "gateway-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"] }), _jsx("span", { className: "text-xs text-muted-foreground", children: selection
|
|
23
|
+
? `共 ${catalog.length} · 已发布 ${publishedCount} · 已失效 ${unavailableCount}`
|
|
24
|
+
: `共 ${catalog.length} · 已失效 ${unavailableCount}` }), _jsx("span", { className: "text-xs text-muted-foreground", children: "Xpod \u5F53\u524D\u53D1\u5E03\u7684\u6A21\u578B" })] }), _jsx(AiModelSearchInput, { value: search, onChange: setSearch })] }), models === undefined ? (_jsx(AiModelEmptyPanel, { children: "Xpod \u6A21\u578B\u76EE\u5F55\u5C1A\u672A\u5C31\u7EEA" })) : catalog.length === 0 ? (_jsx(AiModelEmptyPanel, { children: "\u6682\u65E0\u53EF\u7528\u6A21\u578B" })) : visibleModels.length === 0 ? (_jsx(AiModelEmptyPanel, { children: "\u672A\u627E\u5230\u5339\u914D\u7684\u6A21\u578B" })) : (_jsx("div", { className: "grid gap-2", children: visibleModels.map((model) => (_jsx(AiModelRow, { label: model.displayName ?? model.id, modelId: model.id, iconTokens: model.iconTokens, enabled: selection?.isSelected(model) ?? false, toggleDisabled: selection?.disabled, onToggle: selection ? () => selection.toggle(model) : undefined, unavailable: model.availability === 'unavailable', badges: (_jsxs(_Fragment, { children: [model.custom ? _jsx(Badge, { variant: "outline", className: "shrink-0 text-[10px] font-normal", children: "\u624B\u5DE5" }) : null, model.availability === 'unavailable' ? (_jsx(Badge, { variant: "destructive", className: "shrink-0 text-[10px] font-normal", children: "\u5DF2\u5931\u6548" })) : null] })) }, model.key))) }))] }));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* One gateway model can be published once per credential/offering, so the
|
|
28
|
+
* read surface folds those entries the same way the provider page does.
|
|
29
|
+
*/
|
|
30
|
+
function aggregateGatewayModels(models) {
|
|
31
|
+
const catalog = new Map();
|
|
32
|
+
for (const entry of models) {
|
|
33
|
+
const model = withCatalogModelId(entry);
|
|
34
|
+
const key = `${model.provider}\0${model.id}`;
|
|
35
|
+
const iconTokens = modelIconTokens(model);
|
|
36
|
+
const searchText = [model.id, model.displayName].filter(Boolean).join('\n').toLocaleLowerCase();
|
|
37
|
+
const existing = catalog.get(key);
|
|
38
|
+
if (!existing) {
|
|
39
|
+
catalog.set(key, { ...model, key, iconTokens, searchText });
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (model.availability !== 'unavailable')
|
|
43
|
+
existing.availability = model.availability;
|
|
44
|
+
if (existing.displayName === undefined && model.displayName !== undefined) {
|
|
45
|
+
existing.displayName = model.displayName;
|
|
46
|
+
}
|
|
47
|
+
existing.iconTokens = [...new Set([...existing.iconTokens, ...iconTokens])];
|
|
48
|
+
existing.searchText += `\n${searchText}`;
|
|
49
|
+
}
|
|
50
|
+
return [...catalog.values()];
|
|
51
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The ⓘ affordance that explains a provider-style page.
|
|
4
|
+
*
|
|
5
|
+
* It owns its `TooltipProvider` on purpose. Radix keeps one "pointer in transit"
|
|
6
|
+
* flag per provider, and every trigger inside that provider ignores pointer
|
|
7
|
+
* moves while the flag is set. Sharing a provider with the model list below the
|
|
8
|
+
* header meant that leaving a capability glyph silently swallowed the next hover
|
|
9
|
+
* of the ⓘ, so it never opened on the pages whose catalog carries capability
|
|
10
|
+
* metadata. An island per ⓘ keeps that state out of everyone else's way.
|
|
11
|
+
*/
|
|
12
|
+
export declare function AiInfoTooltip({ label, lines }: {
|
|
13
|
+
label: string;
|
|
14
|
+
lines: ReactNode[];
|
|
15
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@undefineds.co/shared-ui';
|
|
3
|
+
import { Info } from 'lucide-react';
|
|
4
|
+
/**
|
|
5
|
+
* The ⓘ affordance that explains a provider-style page.
|
|
6
|
+
*
|
|
7
|
+
* It owns its `TooltipProvider` on purpose. Radix keeps one "pointer in transit"
|
|
8
|
+
* flag per provider, and every trigger inside that provider ignores pointer
|
|
9
|
+
* moves while the flag is set. Sharing a provider with the model list below the
|
|
10
|
+
* header meant that leaving a capability glyph silently swallowed the next hover
|
|
11
|
+
* of the ⓘ, so it never opened on the pages whose catalog carries capability
|
|
12
|
+
* metadata. An island per ⓘ keeps that state out of everyone else's way.
|
|
13
|
+
*/
|
|
14
|
+
export function AiInfoTooltip({ label, lines }) {
|
|
15
|
+
return (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": label, 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" }) }) }), _jsx(TooltipContent, { className: "max-w-xs space-y-2 text-xs", children: lines.map((line, index) => _jsx("p", { children: line }, index)) })] }) }));
|
|
16
|
+
}
|