@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
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
import { useSyncExternalStore } from 'react';
|
|
2
2
|
import { AI_CONNECTIONS_PROVIDERS, AiConnectionsRequestError, createAiConnectionsClient, normalizeAiConnectionsThrownError, } from './ai-connections-client.js';
|
|
3
|
+
import { credentialCollectionRuntime, } from './collections.js';
|
|
4
|
+
/**
|
|
5
|
+
* Provider pages explain themselves through the header's ⓘ tooltip, so each
|
|
6
|
+
* description is one line of the same shape — where the models come from and how
|
|
7
|
+
* this deployment can be authorized — instead of a restatement of the name.
|
|
8
|
+
*
|
|
9
|
+
* Connect actions are deliberately absent here: which ways in exist is offering
|
|
10
|
+
* data (`authorizationMethods`), derived by the server per offering, and a
|
|
11
|
+
* provider-level label is exactly how a page could offer a 「登录」 no offering
|
|
12
|
+
* declares.
|
|
13
|
+
*/
|
|
3
14
|
export const PROVIDERS = [
|
|
4
|
-
{ id: 'openai', name: 'OpenAI',
|
|
5
|
-
{ id: 'anthropic', name: 'Anthropic',
|
|
6
|
-
{ id: 'kimi', name: 'Kimi',
|
|
7
|
-
{ id: 'bailian', name: '百炼',
|
|
8
|
-
{ id: 'deepseek', name: 'DeepSeek',
|
|
9
|
-
{ id: 'zhipu', name: '智谱 AI',
|
|
10
|
-
{ id: 'ollama', name: 'Ollama',
|
|
11
|
-
{ id: 'custom', name: 'Custom',
|
|
15
|
+
{ id: 'openai', name: 'OpenAI', description: 'OpenAI 官方 GPT 与推理模型,支持 API Key 与订阅导入。', homeUrl: 'https://openai.com', apiKeyUrl: 'https://platform.openai.com/api-keys', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://api.openai.com/v1' },
|
|
16
|
+
{ id: 'anthropic', name: 'Anthropic', description: 'Anthropic 官方 Claude 模型,支持 API Key 与订阅导入。', homeUrl: 'https://www.anthropic.com', apiKeyUrl: 'https://console.anthropic.com/settings/keys', apiKeyPlaceholder: 'sk-ant-...', defaultBaseUrl: 'https://api.anthropic.com' },
|
|
17
|
+
{ id: 'kimi', name: 'Kimi', description: '月之暗面 Kimi 模型,支持账号订阅、编码套餐与开放平台。', homeUrl: 'https://www.moonshot.cn', apiKeyUrl: 'https://platform.moonshot.cn/console/api-keys', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://api.moonshot.cn/v1' },
|
|
18
|
+
{ id: 'bailian', name: '百炼', description: '阿里云百炼的通义千问等模型,提供按量与多种套餐。', homeUrl: 'https://www.aliyun.com/product/bailian', apiKeyUrl: 'https://bailian.console.aliyun.com/#/api-key', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1' },
|
|
19
|
+
{ id: 'deepseek', name: 'DeepSeek', description: 'DeepSeek 官方模型,API Key 接入,接口兼容 OpenAI。', homeUrl: 'https://www.deepseek.com', apiKeyUrl: 'https://platform.deepseek.com/api_keys', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://api.deepseek.com/v1' },
|
|
20
|
+
{ id: 'zhipu', name: '智谱 AI', description: '智谱 GLM 系列模型,支持 API Key 与 GLM 编码套餐。', homeUrl: 'https://open.bigmodel.cn', apiKeyUrl: 'https://open.bigmodel.cn/usercenter/apikeys', apiKeyPlaceholder: 'id.secret-...', defaultBaseUrl: 'https://open.bigmodel.cn/api/paas/v4' },
|
|
21
|
+
{ id: 'ollama', name: 'Ollama', description: '运行在本机的 Ollama 模型,无需 API Key,仅本机可达。', homeUrl: 'https://ollama.com', defaultBaseUrl: 'http://localhost:11434/v1' },
|
|
22
|
+
{ id: 'custom', name: 'Custom', description: '任意 OpenAI 或 Anthropic 兼容服务,自填地址与 API Key。', homeUrl: 'https://undefineds.co', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://example.com/v1' },
|
|
12
23
|
];
|
|
13
24
|
export const AI_CONNECTIONS_PINNED_SECTIONS = [
|
|
14
|
-
{ id: 'keys', label: '
|
|
25
|
+
{ id: 'keys', label: 'Xpod', title: 'API KEYS' },
|
|
15
26
|
];
|
|
27
|
+
/**
|
|
28
|
+
* Coalescing window for Pod change signals.
|
|
29
|
+
*
|
|
30
|
+
* A notification only says "this document changed", so a burst of writes must
|
|
31
|
+
* cost one re-read, not one per write; the first signal arms a fixed window and
|
|
32
|
+
* everything arriving inside it is absorbed.
|
|
33
|
+
*/
|
|
34
|
+
export const TABLE_CHANGE_COALESCE_MS = 75;
|
|
16
35
|
if (PROVIDERS.map((provider) => provider.id).join(',') !== AI_CONNECTIONS_PROVIDERS.join(',')) {
|
|
17
36
|
throw new Error('AI Connection provider UI is out of sync with the client catalog');
|
|
18
37
|
}
|
|
@@ -24,6 +43,28 @@ export function createAiConnectionsController(host) {
|
|
|
24
43
|
const readyPod = pod?.status === 'ready' ? pod : undefined;
|
|
25
44
|
const authenticated = sessionSnapshot.status === 'authenticated'
|
|
26
45
|
&& readyPod !== undefined;
|
|
46
|
+
/**
|
|
47
|
+
* The live credentials table, when the host offers one.
|
|
48
|
+
*
|
|
49
|
+
* Declaring it here (and not in a page) is what keeps one table to one
|
|
50
|
+
* collection: the page reads the same instance the client writes through.
|
|
51
|
+
*
|
|
52
|
+
* It is declared *asynchronously* and lands in `credentials` (+ `credentialRows`
|
|
53
|
+
* and `credentialMutations`): the collection layer is loaded on demand through
|
|
54
|
+
* `collections.credentialCollectionRuntime()` - the table declaration, the row
|
|
55
|
+
* read and the credential writes all live in that chunk - so the applet renders
|
|
56
|
+
* from the store first and upgrades to live rows when the collection's first
|
|
57
|
+
* read is back. When the host offers no `podCollections` capability at all the
|
|
58
|
+
* collection never lands and the controller behaves exactly as before
|
|
59
|
+
* (`docs/pod-collections.md` §8.9).
|
|
60
|
+
*/
|
|
61
|
+
let credentials;
|
|
62
|
+
let credentialRows;
|
|
63
|
+
let credentialMutations;
|
|
64
|
+
const liveCredentials = {
|
|
65
|
+
collection: () => credentials,
|
|
66
|
+
mutations: () => credentialMutations,
|
|
67
|
+
};
|
|
27
68
|
let providerLoadGeneration = 0;
|
|
28
69
|
let providerLoadPromise;
|
|
29
70
|
let providerLoadOperation;
|
|
@@ -81,7 +122,7 @@ export function createAiConnectionsController(host) {
|
|
|
81
122
|
webId: sessionSnapshot.webId,
|
|
82
123
|
podBaseUrl: readyPod.current.podUrl,
|
|
83
124
|
authenticatedFetch: host.solid.session.fetch,
|
|
84
|
-
}), host.capabilities.aiClientCredentials), host.capabilities.aiConnectionsPodStore, beginProviderLoad)
|
|
125
|
+
}), host.capabilities.aiClientCredentials), host.capabilities.aiConnectionsPodStore, liveCredentials, beginProviderLoad)
|
|
85
126
|
: null;
|
|
86
127
|
let selectedSection = 'keys';
|
|
87
128
|
let selectedProvider = 'openai';
|
|
@@ -92,10 +133,164 @@ export function createAiConnectionsController(host) {
|
|
|
92
133
|
let providerLoadError;
|
|
93
134
|
const listeners = new Set();
|
|
94
135
|
const notify = () => listeners.forEach((listener) => listener());
|
|
136
|
+
const notifications = host.capabilities.solidNotifications;
|
|
137
|
+
const podStore = host.capabilities.aiConnectionsPodStore;
|
|
138
|
+
/** Release function per watched topic document; one entry per document. */
|
|
139
|
+
const tableSubscriptions = new Map();
|
|
140
|
+
let pageTableHolds = 0;
|
|
141
|
+
let liveUpdates = currentLiveUpdates();
|
|
142
|
+
let liveRevision = 0;
|
|
143
|
+
let coalesceTimer;
|
|
144
|
+
function scheduleLiveRefresh() {
|
|
145
|
+
if (coalesceTimer !== undefined)
|
|
146
|
+
return;
|
|
147
|
+
coalesceTimer = setTimeout(() => {
|
|
148
|
+
coalesceTimer = undefined;
|
|
149
|
+
liveRevision += 1;
|
|
150
|
+
notify();
|
|
151
|
+
}, TABLE_CHANGE_COALESCE_MS);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The documents the open page renders *and* this controller refreshes; a table
|
|
155
|
+
* is one document, never a row.
|
|
156
|
+
*
|
|
157
|
+
* One table has exactly one refresh path. The credentials table is owned by
|
|
158
|
+
* the collection when the host exposes one - the collection subscribes to the
|
|
159
|
+
* same notification primitive with the same document - so it is left out here;
|
|
160
|
+
* without a collection it stays on this path, exactly as before. The open
|
|
161
|
+
* provider's document is always this path's (P4 moves it).
|
|
162
|
+
*/
|
|
163
|
+
function pageTableDocuments() {
|
|
164
|
+
if (!podStore)
|
|
165
|
+
return [];
|
|
166
|
+
const documents = new Set();
|
|
167
|
+
const credentialsDocument = credentials ? undefined : podStore.credentialsTableDocument?.();
|
|
168
|
+
if (credentialsDocument)
|
|
169
|
+
documents.add(credentialsDocument);
|
|
170
|
+
if (selectedSection === 'provider') {
|
|
171
|
+
const provider = podStore.providerTableDocument?.(selectedProvider, selectedCredentialId);
|
|
172
|
+
if (provider)
|
|
173
|
+
documents.add(provider);
|
|
174
|
+
}
|
|
175
|
+
return [...documents];
|
|
176
|
+
}
|
|
177
|
+
function releaseTableSubscriptions() {
|
|
178
|
+
for (const [document, release] of tableSubscriptions) {
|
|
179
|
+
tableSubscriptions.delete(document);
|
|
180
|
+
release();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function syncTableSubscriptions() {
|
|
184
|
+
if (!notifications || pageTableHolds === 0)
|
|
185
|
+
return;
|
|
186
|
+
const wanted = new Set(pageTableDocuments());
|
|
187
|
+
for (const [document, release] of tableSubscriptions) {
|
|
188
|
+
if (wanted.has(document))
|
|
189
|
+
continue;
|
|
190
|
+
tableSubscriptions.delete(document);
|
|
191
|
+
release();
|
|
192
|
+
}
|
|
193
|
+
for (const document of wanted) {
|
|
194
|
+
if (tableSubscriptions.has(document))
|
|
195
|
+
continue;
|
|
196
|
+
tableSubscriptions.set(document, notifications.watch(document, scheduleLiveRefresh));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The availability signal the page shows.
|
|
201
|
+
*
|
|
202
|
+
* With a live collection the page's truth is the *collection's* sync state,
|
|
203
|
+
* not the transport's global state: `live` only when the table's own feed is
|
|
204
|
+
* established and the transport is actually pushing. Without one, this is the
|
|
205
|
+
* transport's state, exactly as before.
|
|
206
|
+
*/
|
|
207
|
+
function currentLiveUpdates() {
|
|
208
|
+
const transport = notifications ? notifications.getState() : 'unavailable';
|
|
209
|
+
if (!credentials || !host.capabilities.podCollections)
|
|
210
|
+
return transport;
|
|
211
|
+
const sync = host.capabilities.podCollections.syncState(credentials.collection);
|
|
212
|
+
if (sync === 'unavailable' || sync === 'degraded' || transport === 'unavailable') {
|
|
213
|
+
return 'unavailable';
|
|
214
|
+
}
|
|
215
|
+
return sync === 'live' && transport === 'live' ? 'live' : 'idle';
|
|
216
|
+
}
|
|
217
|
+
notifications?.subscribeState(() => {
|
|
218
|
+
const next = currentLiveUpdates();
|
|
219
|
+
if (next === liveUpdates)
|
|
220
|
+
return;
|
|
221
|
+
liveUpdates = next;
|
|
222
|
+
notify();
|
|
223
|
+
});
|
|
224
|
+
host.capabilities.podCollections?.subscribeSyncState(() => {
|
|
225
|
+
const next = currentLiveUpdates();
|
|
226
|
+
if (next === liveUpdates)
|
|
227
|
+
return;
|
|
228
|
+
liveUpdates = next;
|
|
229
|
+
notify();
|
|
230
|
+
});
|
|
231
|
+
/**
|
|
232
|
+
* The page's live rows: re-read from the collection when it changes, and
|
|
233
|
+
* `undefined` until its first read lands (or forever, when there is no
|
|
234
|
+
* collection). One snapshot object per change keeps `useSyncExternalStore`
|
|
235
|
+
* stable between reads.
|
|
236
|
+
*/
|
|
237
|
+
function publishCredentialRows() {
|
|
238
|
+
const collection = credentials?.collection;
|
|
239
|
+
const next = collection?.isReady()
|
|
240
|
+
? collection.toArray
|
|
241
|
+
: undefined;
|
|
242
|
+
if (next === credentialRows)
|
|
243
|
+
return;
|
|
244
|
+
credentialRows = next;
|
|
245
|
+
notify();
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Adopt the host's credentials collection once the lazy layer has it.
|
|
249
|
+
*
|
|
250
|
+
* Three things change at once, and they have to change together: the rows the
|
|
251
|
+
* page renders (from the collection's own change feed and status), the
|
|
252
|
+
* credential mutations (the client dispatches to them from now on), and the
|
|
253
|
+
* fallback table watch - the credentials document leaves the controller's
|
|
254
|
+
* `liveRevision` path because the collection now owns that table, which has to
|
|
255
|
+
* happen by releasing the subscription the controller opened before the
|
|
256
|
+
* collection arrived.
|
|
257
|
+
*/
|
|
258
|
+
function adoptCredentials(loaded, mutations) {
|
|
259
|
+
credentials = loaded;
|
|
260
|
+
credentialMutations = mutations;
|
|
261
|
+
const collection = loaded.collection;
|
|
262
|
+
// The collection is owned by the host capability and lives as long as the
|
|
263
|
+
// session; these two subscriptions live with it rather than with the page.
|
|
264
|
+
collection.subscribeChanges(() => publishCredentialRows());
|
|
265
|
+
collection.on('status:change', () => publishCredentialRows());
|
|
266
|
+
publishCredentialRows();
|
|
267
|
+
syncTableSubscriptions();
|
|
268
|
+
notify();
|
|
269
|
+
}
|
|
270
|
+
// A host that offers no collection never fetches the layer: the capability is
|
|
271
|
+
// checked before the dynamic import, not inside it.
|
|
272
|
+
if (authenticated && sessionSnapshot.webId && host.capabilities.podCollections) {
|
|
273
|
+
void credentialCollectionRuntime()
|
|
274
|
+
.then(async (runtime) => {
|
|
275
|
+
const loaded = await runtime.openCredentialCollection(host);
|
|
276
|
+
if (!loaded || !podStore)
|
|
277
|
+
return;
|
|
278
|
+
adoptCredentials(loaded, runtime.collectionCredentialMutations(loaded, podStore));
|
|
279
|
+
})
|
|
280
|
+
// No collection layer (chunk unavailable, layout underivable, table
|
|
281
|
+
// undefined): the page keeps the store's own reads, as before.
|
|
282
|
+
.catch(() => undefined);
|
|
283
|
+
}
|
|
95
284
|
const controller = {
|
|
96
285
|
client,
|
|
97
286
|
openExternal: host.navigation.openExternal,
|
|
98
287
|
clientConfigurationBridge: host.capabilities.aiClientConfiguration,
|
|
288
|
+
get credentialsCollection() {
|
|
289
|
+
return credentials?.collection;
|
|
290
|
+
},
|
|
291
|
+
get credentialRows() {
|
|
292
|
+
return credentialRows;
|
|
293
|
+
},
|
|
99
294
|
get selectedSection() {
|
|
100
295
|
return selectedSection;
|
|
101
296
|
},
|
|
@@ -117,10 +312,32 @@ export function createAiConnectionsController(host) {
|
|
|
117
312
|
get providerLoadError() {
|
|
118
313
|
return providerLoadError;
|
|
119
314
|
},
|
|
315
|
+
get liveUpdates() {
|
|
316
|
+
return liveUpdates;
|
|
317
|
+
},
|
|
318
|
+
get liveRevision() {
|
|
319
|
+
return liveRevision;
|
|
320
|
+
},
|
|
321
|
+
watchPageTables() {
|
|
322
|
+
pageTableHolds += 1;
|
|
323
|
+
syncTableSubscriptions();
|
|
324
|
+
let released = false;
|
|
325
|
+
return () => {
|
|
326
|
+
if (released)
|
|
327
|
+
return;
|
|
328
|
+
released = true;
|
|
329
|
+
pageTableHolds -= 1;
|
|
330
|
+
if (pageTableHolds > 0)
|
|
331
|
+
return;
|
|
332
|
+
pageTableHolds = 0;
|
|
333
|
+
releaseTableSubscriptions();
|
|
334
|
+
};
|
|
335
|
+
},
|
|
120
336
|
selectSection(section) {
|
|
121
337
|
if (selectedSection === section)
|
|
122
338
|
return;
|
|
123
339
|
selectedSection = section;
|
|
340
|
+
syncTableSubscriptions();
|
|
124
341
|
notify();
|
|
125
342
|
},
|
|
126
343
|
selectProvider(provider, credentialId) {
|
|
@@ -132,6 +349,7 @@ export function createAiConnectionsController(host) {
|
|
|
132
349
|
selectedSection = 'provider';
|
|
133
350
|
selectedProvider = provider;
|
|
134
351
|
selectedCredentialId = nextCredentialId;
|
|
352
|
+
syncTableSubscriptions();
|
|
135
353
|
notify();
|
|
136
354
|
},
|
|
137
355
|
selectFirstUnconfiguredProvider() {
|
|
@@ -255,7 +473,7 @@ function withAccountClientCredentials(client, credentials) {
|
|
|
255
473
|
},
|
|
256
474
|
};
|
|
257
475
|
}
|
|
258
|
-
function createInteractiveAiConnectionsClient(operationsClient, podStore, beginProviderLoad) {
|
|
476
|
+
function createInteractiveAiConnectionsClient(operationsClient, podStore, live, beginProviderLoad) {
|
|
259
477
|
const listProviders = async () => {
|
|
260
478
|
const guard = beginProviderLoad();
|
|
261
479
|
try {
|
|
@@ -327,7 +545,7 @@ function createInteractiveAiConnectionsClient(operationsClient, podStore, beginP
|
|
|
327
545
|
}
|
|
328
546
|
return { credential, secret, didRefresh };
|
|
329
547
|
};
|
|
330
|
-
|
|
548
|
+
const storeClient = {
|
|
331
549
|
...operationsClient,
|
|
332
550
|
listProviders,
|
|
333
551
|
listModels: podStore.listModels
|
|
@@ -556,6 +774,35 @@ function createInteractiveAiConnectionsClient(operationsClient, podStore, beginP
|
|
|
556
774
|
? async (provider, modelIds, credentialId) => podStore.saveModelSelection(provider, modelIds, credentialId)
|
|
557
775
|
: operationsClient.saveModelSelection,
|
|
558
776
|
};
|
|
777
|
+
/**
|
|
778
|
+
* With a live credentials collection, credential writes go through it: the
|
|
779
|
+
* page sees them immediately, they roll back on rejection, and the document's
|
|
780
|
+
* own echo reconciles instead of overwriting.
|
|
781
|
+
*
|
|
782
|
+
* The collection may not be there yet (the layer loads lazily) and may never
|
|
783
|
+
* be (a host without the capability), so this dispatches per call: until the
|
|
784
|
+
* table lands, every write is the store's own, exactly as before.
|
|
785
|
+
*
|
|
786
|
+
* Reads of credentials stay in `listProviders` for now (it also carries
|
|
787
|
+
* providers and models); the page renders the credential lists from the
|
|
788
|
+
* collection's rows, and the store's summary of the same row is only the
|
|
789
|
+
* enrichment for attributes the descriptor cannot project.
|
|
790
|
+
*/
|
|
791
|
+
const credentialStore = {
|
|
792
|
+
createApiKeyCredential: (provider, input) => live.mutations()?.createApiKeyCredential(provider, input)
|
|
793
|
+
?? storeClient.createApiKeyCredential(provider, input),
|
|
794
|
+
// Two local-connection flows are account/Gateway operations, not Pod rows.
|
|
795
|
+
createLocalCredential: (provider, input) => (input.authorizationMethodId === 'local-session-import'
|
|
796
|
+
|| (!input.authorizationMethodId && input.offeringId === 'official-subscription')
|
|
797
|
+
? storeClient.createLocalCredential(provider, input)
|
|
798
|
+
: live.mutations()?.createLocalCredential(provider, input)
|
|
799
|
+
?? storeClient.createLocalCredential(provider, input)),
|
|
800
|
+
updateProviderCredential: (provider, credentialId, input) => live.mutations()?.updateProviderCredential(provider, credentialId, input)
|
|
801
|
+
?? storeClient.updateProviderCredential(provider, credentialId, input),
|
|
802
|
+
deleteProviderCredential: (provider, credentialId) => live.mutations()?.deleteProviderCredential(provider, credentialId)
|
|
803
|
+
?? storeClient.deleteProviderCredential(provider, credentialId),
|
|
804
|
+
};
|
|
805
|
+
return { ...storeClient, ...credentialStore };
|
|
559
806
|
}
|
|
560
807
|
export function useSelectedSection(controller) {
|
|
561
808
|
return useSyncExternalStore(controller.subscribe, () => controller.selectedSection ?? 'provider', () => controller.selectedSection ?? 'provider');
|
|
@@ -575,12 +822,33 @@ export function useProviderStates(controller) {
|
|
|
575
822
|
export function useProviderSummaries(controller) {
|
|
576
823
|
return useSyncExternalStore(controller.subscribe, () => legacySummariesFor(controller.providerSummaries), () => legacySummariesFor(controller.providerSummaries));
|
|
577
824
|
}
|
|
825
|
+
/**
|
|
826
|
+
* Providers this deployment offers, as reported by the server.
|
|
827
|
+
*
|
|
828
|
+
* Cloud answers with the operator-designated providers only (no self-hosted
|
|
829
|
+
* `custom`, no local daemon), so the settings surface cannot offer a provider the
|
|
830
|
+
* deployment does not provide. Before the first successful load this is
|
|
831
|
+
* `undefined`, which keeps every provider visible (Local behavior).
|
|
832
|
+
*/
|
|
833
|
+
export function useAvailableProviders(controller) {
|
|
834
|
+
const summaries = useProviderSummaries(controller);
|
|
835
|
+
const ids = Object.keys(summaries);
|
|
836
|
+
return ids.length > 0 ? ids : undefined;
|
|
837
|
+
}
|
|
578
838
|
export function useProviderProducts(controller) {
|
|
579
839
|
return useSyncExternalStore(controller.subscribe, () => controller.providerSummaries, () => controller.providerSummaries);
|
|
580
840
|
}
|
|
581
841
|
export function useProviderLoadError(controller) {
|
|
582
842
|
return useSyncExternalStore(controller.subscribe, () => controller.providerLoadError, () => controller.providerLoadError);
|
|
583
843
|
}
|
|
844
|
+
/** Whether live Pod updates are on, for a status affordance that stays small. */
|
|
845
|
+
export function useLiveUpdates(controller) {
|
|
846
|
+
return useSyncExternalStore(controller.subscribe, () => controller.liveUpdates, () => controller.liveUpdates);
|
|
847
|
+
}
|
|
848
|
+
/** Advances once per coalesced burst of Pod changes; drives page re-reads. */
|
|
849
|
+
export function useLiveRevision(controller) {
|
|
850
|
+
return useSyncExternalStore(controller.subscribe, () => controller.liveRevision, () => controller.liveRevision);
|
|
851
|
+
}
|
|
584
852
|
function productStateFromProvider(product) {
|
|
585
853
|
if (!product || product.status === 'unconfigured' || product.status === 'unavailable') {
|
|
586
854
|
return 'unconfigured';
|
|
@@ -660,14 +928,15 @@ function mergeAuthorizationMethodsIntoProviders(providers, methods) {
|
|
|
660
928
|
const capability = methodsByOffering.get(`${provider.id}:${offering.id}`);
|
|
661
929
|
if (!capability)
|
|
662
930
|
return offering;
|
|
931
|
+
// The server's list is authoritative, an empty one included: it omits a
|
|
932
|
+
// connect entry this build has not implemented, and that omission has to
|
|
933
|
+
// survive the merge rather than fall back to deriving one from `authModes`.
|
|
663
934
|
const authorizationMethods = capability.authorizationMethods;
|
|
664
935
|
return {
|
|
665
936
|
...offering,
|
|
666
937
|
...(capability.endpoints ? { endpoints: capability.endpoints } : {}),
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
authModes: mergeAuthModes(offering.authModes, authorizationMethods.map((method) => method.authMode)),
|
|
670
|
-
} : {}),
|
|
938
|
+
authorizationMethods,
|
|
939
|
+
authModes: mergeAuthModes(offering.authModes, authorizationMethods.map((method) => method.authMode)),
|
|
671
940
|
};
|
|
672
941
|
}),
|
|
673
942
|
}));
|
|
@@ -5,6 +5,11 @@ export declare function credentialDisplayLabel(credential: AiProviderCredentialS
|
|
|
5
5
|
/** Account names are shown inside a pool; only the domain stays readable. */
|
|
6
6
|
export declare function maskAccountLabel(value: string): string;
|
|
7
7
|
export declare function healthLabel(health: AiProviderCredentialSummary['health']): string;
|
|
8
|
+
/**
|
|
9
|
+
* Tone of one credential row: the tint the row carries and the colour of its
|
|
10
|
+
* health dot. The row's border and dividers belong to the list container now, so
|
|
11
|
+
* the tone is a background rather than a box.
|
|
12
|
+
*/
|
|
8
13
|
export declare function healthTone(health: AiProviderCredentialSummary['health']): {
|
|
9
14
|
row: string;
|
|
10
15
|
dot: string;
|
|
@@ -36,12 +36,17 @@ export function healthLabel(health) {
|
|
|
36
36
|
return '已过期';
|
|
37
37
|
return '错误';
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Tone of one credential row: the tint the row carries and the colour of its
|
|
41
|
+
* health dot. The row's border and dividers belong to the list container now, so
|
|
42
|
+
* the tone is a background rather than a box.
|
|
43
|
+
*/
|
|
39
44
|
export function healthTone(health) {
|
|
40
45
|
if (health === 'healthy') {
|
|
41
|
-
return { row: '
|
|
46
|
+
return { row: 'bg-emerald-500/5', dot: 'bg-emerald-500' };
|
|
42
47
|
}
|
|
43
48
|
if (health === 'unknown') {
|
|
44
|
-
return { row: '
|
|
49
|
+
return { row: 'bg-background', dot: 'bg-muted-foreground/50' };
|
|
45
50
|
}
|
|
46
|
-
return { row: '
|
|
51
|
+
return { row: 'bg-destructive/5', dot: 'bg-destructive' };
|
|
47
52
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface CredentialSecretEnvelopeInput {
|
|
2
|
+
webId: string;
|
|
3
|
+
provider: string;
|
|
4
|
+
/** The credential's resource IRI (`credentialResource.buildIri(podUrl, { id })`). */
|
|
5
|
+
credentialIri: string;
|
|
6
|
+
secret: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The `encryptedSecret` payload for a credential row.
|
|
10
|
+
*
|
|
11
|
+
* `PLAINTEXT` means "not encrypted at rest yet", not "no envelope": the read
|
|
12
|
+
* side verifies the envelope's owner (`webId`), provider and credential IRI
|
|
13
|
+
* before handing the secret to a provider request, so a row copied between
|
|
14
|
+
* credentials (or between accounts) never yields a usable secret.
|
|
15
|
+
*/
|
|
16
|
+
export declare function credentialSecretEnvelope(input: CredentialSecretEnvelopeInput): string;
|
|
17
|
+
export interface CredentialSecretReadInput {
|
|
18
|
+
webId: string;
|
|
19
|
+
provider: string;
|
|
20
|
+
credentialIri: string;
|
|
21
|
+
envelope: unknown;
|
|
22
|
+
}
|
|
23
|
+
/** `undefined` unless the envelope belongs to this account, provider and credential. */
|
|
24
|
+
export declare function decodeCredentialSecret(input: CredentialSecretReadInput): Record<string, unknown> | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The provider relation a catalog credential declares (`provider`):
|
|
27
|
+
* `providers/openai.ttl` in resource terms, `openai.ttl` as the stored value.
|
|
28
|
+
*
|
|
29
|
+
* A custom instance owns its own provider document, so there the relation is
|
|
30
|
+
* derived from the credential's own id.
|
|
31
|
+
*/
|
|
32
|
+
export declare function credentialProviderRelation(provider: string): string;
|
|
33
|
+
export declare function customCredentialProviderRelation(credentialId: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* The row key of a new credential: the `{id}` slot of the descriptor's
|
|
36
|
+
* `resourceIdPattern`, which is the credential's identity (never a display
|
|
37
|
+
* name).
|
|
38
|
+
*
|
|
39
|
+
* Two writers create credential rows - `XpodAiConnectionsPodStore` for a plain
|
|
40
|
+
* page, and the collection layer when a live collection creates the row
|
|
41
|
+
* optimistically - and a row key that differed between them would produce two
|
|
42
|
+
* credentials where the user asked for one, so the shape is declared once here.
|
|
43
|
+
*/
|
|
44
|
+
export declare function credentialRowKeyFor(provider: string, authMode: 'apiKey' | 'local'): string;
|
|
45
|
+
/**
|
|
46
|
+
* The provider a credential row key declares, for rows whose `provider` relation
|
|
47
|
+
* is missing: a credential is keyed `<provider>-<uuid>` (see
|
|
48
|
+
* {@link credentialRowKeyFor}), and a custom instance is keyed `custom-…`.
|
|
49
|
+
*
|
|
50
|
+
* A key reaches us in either of two shapes - the bare `resourceIdPattern` slot
|
|
51
|
+
* (`openai-<uuid>`) or a full resource id (`custom-instance-abc.ttl#this`). The
|
|
52
|
+
* provider lives in the document segment in both, never in the fragment, so the
|
|
53
|
+
* fragment must not be the part that gets inspected.
|
|
54
|
+
*/
|
|
55
|
+
export declare function providerOfCredentialKey(key: string): string | undefined;
|
|
56
|
+
/** `providers/openai.ttl#this` → `openai.ttl#this` (document-relative reference). */
|
|
57
|
+
export declare function providerResourceReference(value: string | undefined): string | undefined;
|
|
58
|
+
/** `providers/openai.ttl#this` → `openai` (the document name minus `.ttl`). */
|
|
59
|
+
export declare function providerResourceKey(value: string | undefined): string | undefined;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { aiProviderResource } from '@undefineds.co/models';
|
|
2
|
+
import { AI_CONNECTIONS_PROVIDERS } from './client/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Storage-shape rules for AI credential rows: the secret envelope, the provider
|
|
5
|
+
* relation a credential declares, and the provider resource key.
|
|
6
|
+
*
|
|
7
|
+
* They live in the capability package, not in the Pod adapter that happens to
|
|
8
|
+
* use them: `@undefineds.co/ai-connections` owns what a credential row looks
|
|
9
|
+
* like, and `ui/src/extensions/XpodAiConnectionsPodStore.ts` is one adapter over
|
|
10
|
+
* it. Keeping one copy is what lets the collection layer write the same rows the
|
|
11
|
+
* adapter reads - a second copy would drift on the first format change.
|
|
12
|
+
*/
|
|
13
|
+
/** Base64 of the UTF-8 JSON, so the envelope stays a single string literal. */
|
|
14
|
+
function encodeBase64Json(value) {
|
|
15
|
+
const bytes = new TextEncoder().encode(JSON.stringify(value));
|
|
16
|
+
let binary = '';
|
|
17
|
+
for (const byte of bytes)
|
|
18
|
+
binary += String.fromCharCode(byte);
|
|
19
|
+
return btoa(binary);
|
|
20
|
+
}
|
|
21
|
+
function decodeBase64Json(value) {
|
|
22
|
+
const binary = atob(value);
|
|
23
|
+
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
24
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The `encryptedSecret` payload for a credential row.
|
|
28
|
+
*
|
|
29
|
+
* `PLAINTEXT` means "not encrypted at rest yet", not "no envelope": the read
|
|
30
|
+
* side verifies the envelope's owner (`webId`), provider and credential IRI
|
|
31
|
+
* before handing the secret to a provider request, so a row copied between
|
|
32
|
+
* credentials (or between accounts) never yields a usable secret.
|
|
33
|
+
*/
|
|
34
|
+
export function credentialSecretEnvelope(input) {
|
|
35
|
+
return JSON.stringify({
|
|
36
|
+
algorithm: 'PLAINTEXT',
|
|
37
|
+
encoding: 'base64',
|
|
38
|
+
ciphertext: encodeBase64Json(input.secret),
|
|
39
|
+
webId: input.webId,
|
|
40
|
+
credentialIri: input.credentialIri,
|
|
41
|
+
provider: input.provider,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/** `undefined` unless the envelope belongs to this account, provider and credential. */
|
|
45
|
+
export function decodeCredentialSecret(input) {
|
|
46
|
+
if (typeof input.envelope !== 'string' || !input.envelope.trim())
|
|
47
|
+
return undefined;
|
|
48
|
+
try {
|
|
49
|
+
const envelope = JSON.parse(input.envelope);
|
|
50
|
+
if (envelope.algorithm !== 'PLAINTEXT'
|
|
51
|
+
|| envelope.webId !== input.webId
|
|
52
|
+
|| envelope.provider !== input.provider
|
|
53
|
+
|| envelope.credentialIri !== input.credentialIri) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const secret = envelope.encoding === 'base64'
|
|
57
|
+
? decodeBase64Json(String(envelope.ciphertext))
|
|
58
|
+
: JSON.parse(String(envelope.ciphertext));
|
|
59
|
+
return typeof secret === 'object' && secret !== null && !Array.isArray(secret)
|
|
60
|
+
? secret
|
|
61
|
+
: undefined;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The provider relation a catalog credential declares (`provider`):
|
|
69
|
+
* `providers/openai.ttl` in resource terms, `openai.ttl` as the stored value.
|
|
70
|
+
*
|
|
71
|
+
* A custom instance owns its own provider document, so there the relation is
|
|
72
|
+
* derived from the credential's own id.
|
|
73
|
+
*/
|
|
74
|
+
export function credentialProviderRelation(provider) {
|
|
75
|
+
return aiProviderResource.buildId({ id: provider });
|
|
76
|
+
}
|
|
77
|
+
export function customCredentialProviderRelation(credentialId) {
|
|
78
|
+
return aiProviderResource.buildId({ id: `custom-instance-${encodeURIComponent(credentialId)}.ttl#this` });
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The row key of a new credential: the `{id}` slot of the descriptor's
|
|
82
|
+
* `resourceIdPattern`, which is the credential's identity (never a display
|
|
83
|
+
* name).
|
|
84
|
+
*
|
|
85
|
+
* Two writers create credential rows - `XpodAiConnectionsPodStore` for a plain
|
|
86
|
+
* page, and the collection layer when a live collection creates the row
|
|
87
|
+
* optimistically - and a row key that differed between them would produce two
|
|
88
|
+
* credentials where the user asked for one, so the shape is declared once here.
|
|
89
|
+
*/
|
|
90
|
+
export function credentialRowKeyFor(provider, authMode) {
|
|
91
|
+
const suffix = crypto.randomUUID();
|
|
92
|
+
return authMode === 'local' ? `${provider}-local-${suffix}` : `${provider}-${suffix}`;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The provider a credential row key declares, for rows whose `provider` relation
|
|
96
|
+
* is missing: a credential is keyed `<provider>-<uuid>` (see
|
|
97
|
+
* {@link credentialRowKeyFor}), and a custom instance is keyed `custom-…`.
|
|
98
|
+
*
|
|
99
|
+
* A key reaches us in either of two shapes - the bare `resourceIdPattern` slot
|
|
100
|
+
* (`openai-<uuid>`) or a full resource id (`custom-instance-abc.ttl#this`). The
|
|
101
|
+
* provider lives in the document segment in both, never in the fragment, so the
|
|
102
|
+
* fragment must not be the part that gets inspected.
|
|
103
|
+
*/
|
|
104
|
+
export function providerOfCredentialKey(key) {
|
|
105
|
+
const document = (key.split('#', 1)[0] ?? key).split('/').filter(Boolean).at(-1) ?? '';
|
|
106
|
+
const name = document.endsWith('.ttl') ? document.slice(0, -'.ttl'.length) : document;
|
|
107
|
+
if (!name)
|
|
108
|
+
return undefined;
|
|
109
|
+
if (name.startsWith('custom-') || name === 'custom')
|
|
110
|
+
return 'custom';
|
|
111
|
+
return AI_CONNECTIONS_PROVIDERS.find((provider) => name === provider || name.startsWith(`${provider}-`));
|
|
112
|
+
}
|
|
113
|
+
/** `providers/openai.ttl#this` → `openai.ttl#this` (document-relative reference). */
|
|
114
|
+
export function providerResourceReference(value) {
|
|
115
|
+
if (!value)
|
|
116
|
+
return undefined;
|
|
117
|
+
const withoutFragment = value.split('#', 1)[0] ?? value;
|
|
118
|
+
const fileName = withoutFragment.split('/').filter(Boolean).at(-1) ?? withoutFragment;
|
|
119
|
+
if (!fileName)
|
|
120
|
+
return undefined;
|
|
121
|
+
const document = fileName.endsWith('.ttl') ? fileName : `${fileName}.ttl`;
|
|
122
|
+
const fragmentIndex = value.indexOf('#');
|
|
123
|
+
return fragmentIndex < 0 ? document : `${document}${value.slice(fragmentIndex)}`;
|
|
124
|
+
}
|
|
125
|
+
/** `providers/openai.ttl#this` → `openai` (the document name minus `.ttl`). */
|
|
126
|
+
export function providerResourceKey(value) {
|
|
127
|
+
const reference = providerResourceReference(value);
|
|
128
|
+
if (!reference)
|
|
129
|
+
return undefined;
|
|
130
|
+
return (reference.split('#', 1)[0] ?? reference).replace(/\.ttl$/u, '');
|
|
131
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The endpoint shape rules every consumer shares: the coding-client adapters
|
|
3
|
+
* (`client-config/base-adapter.ts`) and the UI both point clients at the same
|
|
4
|
+
* Xpod base URL, so the `/v1` rule lives here once and nowhere else.
|
|
5
|
+
*
|
|
6
|
+
* This module must stay dependency-free: it is bundled into the Web applet and
|
|
7
|
+
* imported by the Node-side configuration service.
|
|
8
|
+
*/
|
|
9
|
+
/** OpenAI-compatible base: the client appends `/chat/completions` or `/responses` itself. */
|
|
10
|
+
export declare function normalizeV1Endpoint(endpoint: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Anthropic base: `ANTHROPIC_BASE_URL` is the host; the client appends
|
|
13
|
+
* `/v1/messages`, so a trailing `/v1` has to come off rather than stay on.
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizeMessagesEndpoint(endpoint: string): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The endpoint shape rules every consumer shares: the coding-client adapters
|
|
3
|
+
* (`client-config/base-adapter.ts`) and the UI both point clients at the same
|
|
4
|
+
* Xpod base URL, so the `/v1` rule lives here once and nowhere else.
|
|
5
|
+
*
|
|
6
|
+
* This module must stay dependency-free: it is bundled into the Web applet and
|
|
7
|
+
* imported by the Node-side configuration service.
|
|
8
|
+
*/
|
|
9
|
+
/** OpenAI-compatible base: the client appends `/chat/completions` or `/responses` itself. */
|
|
10
|
+
export function normalizeV1Endpoint(endpoint) {
|
|
11
|
+
const normalized = endpoint.trim().replace(/\/+$/, '');
|
|
12
|
+
return normalized.endsWith('/v1') ? normalized : `${normalized}/v1`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Anthropic base: `ANTHROPIC_BASE_URL` is the host; the client appends
|
|
16
|
+
* `/v1/messages`, so a trailing `/v1` has to come off rather than stay on.
|
|
17
|
+
*/
|
|
18
|
+
export function normalizeMessagesEndpoint(endpoint) {
|
|
19
|
+
return endpoint.trim().replace(/\/+$/, '').replace(/\/v1$/, '');
|
|
20
|
+
}
|