@undefineds.co/xpod 0.4.11 → 0.4.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (372) hide show
  1. package/config/components-ignore.json +5 -1
  2. package/config/notifications.json +68 -0
  3. package/config/xpod.base.json +9 -11
  4. package/dist/ai/service/AiCredentialSecret.d.ts +30 -0
  5. package/dist/ai/service/AiCredentialSecret.js +120 -0
  6. package/dist/ai/service/CredentialReaderImpl.d.ts +18 -0
  7. package/dist/ai/service/CredentialReaderImpl.js +81 -5
  8. package/dist/ai/service/CredentialReaderImpl.jsonld +42 -2
  9. package/dist/ai/service/EmbeddingModelPolicy.d.ts +79 -0
  10. package/dist/ai/service/EmbeddingModelPolicy.js +134 -0
  11. package/dist/ai/service/EmbeddingProviderError.js +6 -0
  12. package/dist/ai/service/EmbeddingServiceImpl.d.ts +13 -1
  13. package/dist/ai/service/EmbeddingServiceImpl.js +18 -5
  14. package/dist/ai/service/EmbeddingServiceImpl.jsonld +35 -0
  15. package/dist/ai/service/index.d.ts +2 -0
  16. package/dist/ai/service/index.js +2 -0
  17. package/dist/api/ai-gateway/connect/index.d.ts +20 -2
  18. package/dist/api/ai-gateway/connect/index.js +162 -16
  19. package/dist/api/ai-gateway/credentials/AiCredentialSecretDecoder.d.ts +12 -0
  20. package/dist/api/ai-gateway/credentials/AiCredentialSecretDecoder.js +30 -0
  21. package/dist/api/ai-gateway/errors.d.ts +1 -1
  22. package/dist/api/ai-gateway/models/GatewayEmbeddingModelCatalog.d.ts +11 -0
  23. package/dist/api/ai-gateway/models/GatewayEmbeddingModelCatalog.js +31 -0
  24. package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +7 -3
  25. package/dist/api/ai-gateway/models/ProviderCustomModelsService.d.ts +18 -0
  26. package/dist/api/ai-gateway/models/ProviderCustomModelsService.js +23 -0
  27. package/dist/api/ai-gateway/models/ProviderModelDiscoveryAdapters.d.ts +3 -1
  28. package/dist/api/ai-gateway/models/ProviderModelDiscoveryAdapters.js +2 -45
  29. package/dist/api/ai-gateway/models/ProviderModelSelectionService.d.ts +10 -0
  30. package/dist/api/ai-gateway/models/ProviderModelSelectionService.js +57 -20
  31. package/dist/api/ai-gateway/models/ProviderModelType.d.ts +28 -0
  32. package/dist/api/ai-gateway/models/ProviderModelType.js +34 -0
  33. package/dist/api/ai-gateway/models/ProviderModelsAdapter.d.ts +10 -0
  34. package/dist/api/ai-gateway/models/ProviderModelsAdapter.js +3 -4
  35. package/dist/api/ai-gateway/models/index.d.ts +1 -0
  36. package/dist/api/ai-gateway/models/index.js +3 -1
  37. package/dist/api/ai-gateway/providers/CustomRuntimeAdapter.d.ts +7 -0
  38. package/dist/api/ai-gateway/providers/CustomRuntimeAdapter.js +9 -2
  39. package/dist/api/ai-gateway/providers/ModelsDevCatalog.d.ts +7 -1
  40. package/dist/api/ai-gateway/providers/ModelsDevCatalog.js +4 -10
  41. package/dist/api/ai-gateway/providers/ProviderRegistry.d.ts +107 -0
  42. package/dist/api/ai-gateway/providers/ProviderRegistry.js +244 -21
  43. package/dist/api/ai-gateway/providers/ProviderRuntimeRegistry.d.ts +5 -0
  44. package/dist/api/ai-gateway/providers/ProviderRuntimeRegistry.js +5 -1
  45. package/dist/api/ai-gateway/quota/SubscriptionQuotaAdapters.d.ts +12 -4
  46. package/dist/api/ai-gateway/quota/SubscriptionQuotaAdapters.js +11 -11
  47. package/dist/api/ai-gateway/routing/ModelRouter.d.ts +14 -0
  48. package/dist/api/ai-gateway/routing/ModelRouter.js +43 -4
  49. package/dist/api/ai-gateway/service-access/AiConnectionsServiceAccess.d.ts +9 -1
  50. package/dist/api/ai-gateway/service-access/AiConnectionsServiceAccess.js +3 -27
  51. package/dist/api/chatkit/pod-store.d.ts +31 -0
  52. package/dist/api/chatkit/pod-store.js +132 -8
  53. package/dist/api/container/common.js +24 -4
  54. package/dist/api/container/routes.js +17 -2
  55. package/dist/api/container/types.d.ts +3 -1
  56. package/dist/api/handlers/AdminHandler.d.ts +13 -0
  57. package/dist/api/handlers/AdminHandler.js +47 -18
  58. package/dist/api/handlers/AiConfigHandler.d.ts +6 -0
  59. package/dist/api/handlers/AiConfigHandler.js +28 -0
  60. package/dist/api/handlers/AiGatewayManagementHandler.js +36 -0
  61. package/dist/api/runs/RdfRunContextRetriever.d.ts +14 -0
  62. package/dist/api/runs/RdfRunContextRetriever.js +41 -5
  63. package/dist/api/runtime.js +2 -1
  64. package/dist/cli/commands/secret.d.ts +1 -0
  65. package/dist/cli/commands/secret.js +25 -7
  66. package/dist/components/components.jsonld +3 -1
  67. package/dist/components/context.jsonld +53 -1
  68. package/dist/http/SubgraphSparqlHttpHandler.d.ts +59 -2
  69. package/dist/http/SubgraphSparqlHttpHandler.js +145 -13
  70. package/dist/http/SubgraphSparqlHttpHandler.jsonld +37 -0
  71. package/dist/identity/drizzle/EdgeNodeRepository.d.ts +0 -12
  72. package/dist/identity/drizzle/EdgeNodeRepository.js +5 -13
  73. package/dist/index.d.ts +2 -1
  74. package/dist/index.js +7 -3
  75. package/dist/logging/log-file.d.ts +17 -0
  76. package/dist/logging/log-file.js +69 -0
  77. package/dist/main.js +2 -1
  78. package/dist/notifications/NotificationChannelSweeper.d.ts +50 -0
  79. package/dist/notifications/NotificationChannelSweeper.js +131 -0
  80. package/dist/notifications/NotificationChannelSweeper.jsonld +136 -0
  81. package/dist/notifications/ReclaimingWebSocket2023Storer.d.ts +25 -0
  82. package/dist/notifications/ReclaimingWebSocket2023Storer.js +56 -0
  83. package/dist/notifications/ReclaimingWebSocket2023Storer.jsonld +77 -0
  84. package/dist/notifications/index.d.ts +2 -0
  85. package/dist/notifications/index.js +2 -0
  86. package/dist/runtime/Proxy.d.ts +37 -0
  87. package/dist/runtime/Proxy.js +88 -16
  88. package/dist/runtime/bootstrap.js +2 -1
  89. package/dist/runtime/upgrade/BunNativeUpgradeRelay.d.ts +116 -0
  90. package/dist/runtime/upgrade/BunNativeUpgradeRelay.js +423 -0
  91. package/dist/runtime/upgrade/UpgradeHandshake.d.ts +63 -0
  92. package/dist/runtime/upgrade/UpgradeHandshake.js +199 -0
  93. package/dist/runtime/upgrade/WebSocketFrames.d.ts +67 -0
  94. package/dist/runtime/upgrade/WebSocketFrames.js +224 -0
  95. package/node_modules/@undefineds.co/ai-connections/dist/AiApiKeyPool.js +8 -1
  96. package/node_modules/@undefineds.co/ai-connections/dist/AiAuthorizationActions.d.ts +11 -2
  97. package/node_modules/@undefineds.co/ai-connections/dist/AiAuthorizationActions.js +27 -10
  98. package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.d.ts +7 -0
  99. package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.js +13 -11
  100. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectDialog.js +1 -1
  101. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsList.js +10 -6
  102. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.d.ts +15 -0
  103. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +29 -3
  104. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMainHeader.js +18 -3
  105. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.d.ts +16 -1
  106. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.js +92 -20
  107. package/node_modules/@undefineds.co/ai-connections/dist/AiCopyButton.d.ts +35 -0
  108. package/node_modules/@undefineds.co/ai-connections/dist/AiCopyButton.js +45 -0
  109. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +71 -11
  110. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialRow.js +9 -7
  111. package/node_modules/@undefineds.co/ai-connections/dist/AiEndpointList.d.ts +23 -0
  112. package/node_modules/@undefineds.co/ai-connections/dist/AiEndpointList.js +24 -0
  113. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.d.ts +13 -4
  114. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +35 -38
  115. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayModelsSection.d.ts +21 -0
  116. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayModelsSection.js +51 -0
  117. package/node_modules/@undefineds.co/ai-connections/dist/AiInfoTooltip.d.ts +15 -0
  118. package/node_modules/@undefineds.co/ai-connections/dist/AiInfoTooltip.js +16 -0
  119. package/node_modules/@undefineds.co/ai-connections/dist/AiModelCatalog.d.ts +175 -0
  120. package/node_modules/@undefineds.co/ai-connections/dist/AiModelCatalog.js +133 -0
  121. package/node_modules/@undefineds.co/ai-connections/dist/AiOfferingDetails.js +2 -2
  122. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.js +13 -64
  123. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderHeader.d.ts +21 -0
  124. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderHeader.js +12 -0
  125. package/node_modules/@undefineds.co/ai-connections/dist/AiRowAction.d.ts +15 -0
  126. package/node_modules/@undefineds.co/ai-connections/dist/AiRowAction.js +12 -0
  127. package/node_modules/@undefineds.co/ai-connections/dist/AiSortableCredentialList.js +2 -1
  128. package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.d.ts +7 -0
  129. package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.js +6 -0
  130. package/node_modules/@undefineds.co/ai-connections/dist/authorization-methods.d.ts +36 -5
  131. package/node_modules/@undefineds.co/ai-connections/dist/authorization-methods.js +52 -9
  132. package/node_modules/@undefineds.co/ai-connections/dist/client/normalize.js +30 -3
  133. package/node_modules/@undefineds.co/ai-connections/dist/client/types.d.ts +21 -1
  134. package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.d.ts +1 -3
  135. package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.js +3 -7
  136. package/node_modules/@undefineds.co/ai-connections/dist/client-config/index.cjs +9 -7
  137. package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.d.ts +46 -0
  138. package/node_modules/@undefineds.co/ai-connections/dist/collection-runtime.js +231 -0
  139. package/node_modules/@undefineds.co/ai-connections/dist/collections.d.ts +119 -0
  140. package/node_modules/@undefineds.co/ai-connections/dist/collections.js +173 -0
  141. package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +71 -5
  142. package/node_modules/@undefineds.co/ai-connections/dist/controller.js +285 -16
  143. package/node_modules/@undefineds.co/ai-connections/dist/credential-labels.d.ts +5 -0
  144. package/node_modules/@undefineds.co/ai-connections/dist/credential-labels.js +8 -3
  145. package/node_modules/@undefineds.co/ai-connections/dist/credential-storage.d.ts +59 -0
  146. package/node_modules/@undefineds.co/ai-connections/dist/credential-storage.js +131 -0
  147. package/node_modules/@undefineds.co/ai-connections/dist/endpoint-urls.d.ts +15 -0
  148. package/node_modules/@undefineds.co/ai-connections/dist/endpoint-urls.js +20 -0
  149. package/node_modules/@undefineds.co/ai-connections/dist/gateway-endpoints.d.ts +14 -0
  150. package/node_modules/@undefineds.co/ai-connections/dist/gateway-endpoints.js +20 -0
  151. package/node_modules/@undefineds.co/ai-connections/dist/index.d.ts +8 -0
  152. package/node_modules/@undefineds.co/ai-connections/dist/index.js +8 -0
  153. package/node_modules/@undefineds.co/ai-connections/dist/manifest.js +1 -1
  154. package/node_modules/@undefineds.co/ai-connections/dist/offering-endpoints.d.ts +14 -0
  155. package/node_modules/@undefineds.co/ai-connections/dist/offering-endpoints.js +17 -0
  156. package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.cjs +68 -1
  157. package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.d.ts +75 -1
  158. package/node_modules/@undefineds.co/ai-connections/dist/provider-catalog.js +119 -0
  159. package/node_modules/@undefineds.co/ai-connections/dist/provider-visuals.d.ts +5 -0
  160. package/node_modules/@undefineds.co/ai-connections/dist/provider-visuals.js +5 -0
  161. package/node_modules/@undefineds.co/ai-connections/dist/service-access.js +2 -27
  162. package/node_modules/@undefineds.co/ai-connections/package.json +1 -0
  163. package/node_modules/@undefineds.co/extension-sdk/dist/manifest.d.ts +1 -1
  164. package/node_modules/@undefineds.co/extension-sdk/dist/manifest.js +2 -0
  165. package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +150 -0
  166. package/node_modules/@undefineds.co/extension-sdk/package.json +2 -1
  167. package/node_modules/@undefineds.co/models/README.md +1 -1
  168. package/node_modules/@undefineds.co/models/dist/ai-config/index.d.ts +3 -3
  169. package/node_modules/@undefineds.co/models/dist/ai-gateway.schema.d.ts +5 -0
  170. package/node_modules/@undefineds.co/models/dist/ai-gateway.schema.js +9 -0
  171. package/node_modules/@undefineds.co/models/dist/ai-runtime.repository.d.ts +1 -1
  172. package/node_modules/@undefineds.co/models/dist/approval.repository.d.ts +2 -2
  173. package/node_modules/@undefineds.co/models/dist/audit.presentation.d.ts +2 -2
  174. package/node_modules/@undefineds.co/models/dist/capture.repository.d.ts +1 -1
  175. package/node_modules/@undefineds.co/models/dist/chat-project.repository.d.ts +1 -1
  176. package/node_modules/@undefineds.co/models/dist/chat.repository.d.ts +2 -2
  177. package/node_modules/@undefineds.co/models/dist/control-request.repository.d.ts +2 -2
  178. package/node_modules/@undefineds.co/models/dist/conversation-share.repository.d.ts +1 -1
  179. package/node_modules/@undefineds.co/models/dist/credential.schema.d.ts +12 -0
  180. package/node_modules/@undefineds.co/models/dist/credential.schema.js +15 -1
  181. package/node_modules/@undefineds.co/models/dist/discovery/index.d.ts +2 -2
  182. package/node_modules/@undefineds.co/models/dist/discovery/providers.json +248 -7
  183. package/node_modules/@undefineds.co/models/dist/discovery/types.d.ts +31 -0
  184. package/node_modules/@undefineds.co/models/dist/extension/index.d.ts +1 -1
  185. package/node_modules/@undefineds.co/models/dist/favorite/index.d.ts +2 -2
  186. package/node_modules/@undefineds.co/models/dist/fixtures/contracts-chat-contact.d.ts +5 -5
  187. package/node_modules/@undefineds.co/models/dist/import/index.d.ts +1 -1
  188. package/node_modules/@undefineds.co/models/dist/index.d.ts +73 -73
  189. package/node_modules/@undefineds.co/models/dist/index.js +1 -1
  190. package/node_modules/@undefineds.co/models/dist/input-request.repository.d.ts +2 -2
  191. package/node_modules/@undefineds.co/models/dist/knowledge/index.d.ts +1 -1
  192. package/node_modules/@undefineds.co/models/dist/message.repository.d.ts +1 -1
  193. package/node_modules/@undefineds.co/models/dist/namespaces.js +5 -0
  194. package/node_modules/@undefineds.co/models/dist/pod-storage-descriptor.d.ts +4815 -26
  195. package/node_modules/@undefineds.co/models/dist/pod-storage-descriptor.js +482 -132
  196. package/node_modules/@undefineds.co/models/dist/schema.d.ts +4 -0
  197. package/node_modules/@undefineds.co/models/dist/session/index.d.ts +1 -1
  198. package/node_modules/@undefineds.co/models/dist/settings/index.d.ts +1 -1
  199. package/node_modules/@undefineds.co/models/dist/sidecar/persistence-mapping.d.ts +5 -5
  200. package/node_modules/@undefineds.co/models/dist/thread.repository.d.ts +2 -2
  201. package/node_modules/@undefineds.co/models/dist/types/message-block.d.ts +1 -1
  202. package/node_modules/@undefineds.co/models/dist/vocab/_namespaces.d.ts +1 -1
  203. package/node_modules/@undefineds.co/models/dist/vocab/index.d.ts +10 -10
  204. package/node_modules/@undefineds.co/models/dist/vocab/sidecar.vocab.d.ts +1 -0
  205. package/node_modules/@undefineds.co/models/dist/vocab/sidecar.vocab.js +1 -0
  206. package/node_modules/@undefineds.co/models/package.json +4 -2
  207. package/node_modules/@undefineds.co/pod-collections/README.md +71 -0
  208. package/node_modules/@undefineds.co/pod-collections/dist/diff.d.ts +85 -0
  209. package/node_modules/@undefineds.co/pod-collections/dist/diff.js +166 -0
  210. package/node_modules/@undefineds.co/pod-collections/dist/feed.d.ts +17 -0
  211. package/node_modules/@undefineds.co/pod-collections/dist/feed.js +27 -0
  212. package/node_modules/@undefineds.co/pod-collections/dist/index.cjs +1042 -0
  213. package/node_modules/@undefineds.co/pod-collections/dist/index.d.ts +37 -0
  214. package/node_modules/@undefineds.co/pod-collections/dist/index.js +89 -0
  215. package/node_modules/@undefineds.co/pod-collections/dist/layout.d.ts +75 -0
  216. package/node_modules/@undefineds.co/pod-collections/dist/layout.js +173 -0
  217. package/node_modules/@undefineds.co/pod-collections/dist/mapping.d.ts +78 -0
  218. package/node_modules/@undefineds.co/pod-collections/dist/mapping.js +287 -0
  219. package/node_modules/@undefineds.co/pod-collections/dist/mutations.d.ts +83 -0
  220. package/node_modules/@undefineds.co/pod-collections/dist/mutations.js +273 -0
  221. package/node_modules/@undefineds.co/pod-collections/dist/react.d.ts +21 -0
  222. package/node_modules/@undefineds.co/pod-collections/dist/react.js +22 -0
  223. package/node_modules/@undefineds.co/pod-collections/dist/read.d.ts +65 -0
  224. package/node_modules/@undefineds.co/pod-collections/dist/read.js +31 -0
  225. package/node_modules/@undefineds.co/pod-collections/dist/sync.d.ts +66 -0
  226. package/node_modules/@undefineds.co/pod-collections/dist/sync.js +260 -0
  227. package/node_modules/@undefineds.co/pod-collections/dist/types.d.ts +155 -0
  228. package/node_modules/@undefineds.co/pod-collections/dist/types.js +9 -0
  229. package/node_modules/@undefineds.co/pod-collections/package.json +42 -0
  230. package/package.json +15 -7
  231. package/static/app/assets/main.css +1 -1
  232. package/static/auth-callback/assets/AiConfigContext-DnQs1ZpW.js +1 -0
  233. package/static/auth-callback/assets/AiConfigPage-DOtvlxH0.js +1 -0
  234. package/static/auth-callback/assets/DocumentProcessingPanel-BS2voQgL.js +1 -0
  235. package/static/auth-callback/assets/IndexLifecyclePanel-Cvt0cJtN.js +1 -0
  236. package/static/auth-callback/assets/{IndexSubjectPanel-BbUqop4b.js → IndexSubjectPanel-Dhf01GqF.js} +1 -1
  237. package/static/auth-callback/assets/ModelAssignmentsPanel-BJutWaAF.js +1 -0
  238. package/static/auth-callback/assets/ModelsPage-BRS0sje3.js +11 -0
  239. package/static/auth-callback/assets/{NetworkPage-Vp2PrMxs.js → NetworkPage-B3qBfANA.js} +2 -2
  240. package/static/auth-callback/assets/{PaneListHeader-DY7baSFl.js → PaneListHeader-QQeJGTp4.js} +1 -1
  241. package/static/auth-callback/assets/SearchIndexingPanel-MyNUSEwT.js +1 -0
  242. package/static/auth-callback/assets/{StatusSubjectPanel-ArIS-TxT.js → StatusSubjectPanel-DQkEVsFs.js} +1 -1
  243. package/static/{settings/assets/StatusWorkspace-CoMFRXoV.js → auth-callback/assets/StatusWorkspace-Dkv63Uwk.js} +1 -1
  244. package/static/auth-callback/assets/{SystemSettingsPage-BDvT1NUz.js → SystemSettingsPage-TIsUfX5d.js} +1 -1
  245. package/static/{dashboard/assets/SystemSettingsSubjectPanel-WaE8mTQi.js → auth-callback/assets/SystemSettingsSubjectPanel-BJWEt2-7.js} +5 -5
  246. package/static/auth-callback/assets/XpodAiConnectionsPodStore-BJiXm-PF.js +3 -0
  247. package/static/auth-callback/assets/{activity-D1Dsfa_0.js → activity-5hNeNKnL.js} +1 -1
  248. package/static/auth-callback/assets/ai-connections-CryuHGKW.js +1 -0
  249. package/static/auth-callback/assets/{auth-callback-Cmf4hXVZ.js → auth-callback-CAFZ3R0B.js} +6 -6
  250. package/static/auth-callback/assets/auth-callback-rk080mvG.css +1 -0
  251. package/static/auth-callback/assets/{badge-CZsHq3Ih.js → badge-CAIPI0CB.js} +1 -1
  252. package/static/auth-callback/assets/collection-runtime-D9s7h9YA.js +1 -0
  253. package/static/auth-callback/assets/dialog-BjA109aX.js +1 -0
  254. package/static/auth-callback/assets/{download-DU9ntlHw.js → download-B3Up5W2G.js} +1 -1
  255. package/static/auth-callback/assets/{external-link-BfzaaZDx.js → external-link-BTXgSPzB.js} +1 -1
  256. package/static/auth-callback/assets/index-BppXlxcp.js +41 -0
  257. package/static/auth-callback/assets/index-CirW4CJf.js +1 -0
  258. package/static/auth-callback/assets/index-DlLYWOhw.js +4 -0
  259. package/static/auth-callback/assets/{index-browser-BNFqqC74.js → index-browser-DtU38ni3.js} +1 -1
  260. package/static/auth-callback/assets/{key-round-BwP3DBWo.js → key-round-CQCOrZ7x.js} +1 -1
  261. package/static/auth-callback/assets/layout-DB3pMyTl.js +1 -0
  262. package/static/auth-callback/assets/pod-collections-host-KnnGFZHL.js +8 -0
  263. package/static/auth-callback/assets/{rotate-ccw-VMwnmyab.js → rotate-ccw-C1zM2KtS.js} +1 -1
  264. package/static/auth-callback/assets/{skeleton-CoD55rBT.js → skeleton-Cqp7_M9-.js} +1 -1
  265. package/static/auth-callback/assets/{waypoints-DRo_1Ca7.js → waypoints-Bj6gAUSQ.js} +1 -1
  266. package/static/auth-callback/assets/{workspace-layout--mcXl3My.js → workspace-layout-DK8mDzrY.js} +1 -1
  267. package/static/auth-callback/auth-callback.html +2 -2
  268. package/static/dashboard/assets/AiConfigContext-BsE7MqX8.js +1 -0
  269. package/static/dashboard/assets/AiConfigPage-DvM-lRXa.js +1 -0
  270. package/static/dashboard/assets/DocumentProcessingPanel-BPfdlfuf.js +1 -0
  271. package/static/dashboard/assets/IndexLifecyclePanel--m0AGxvh.js +1 -0
  272. package/static/dashboard/assets/{IndexSubjectPanel-B2cXwl8X.js → IndexSubjectPanel-Bb7m1k3d.js} +1 -1
  273. package/static/dashboard/assets/ModelAssignmentsPanel-BLi_zbMa.js +1 -0
  274. package/static/dashboard/assets/ModelsPage-CxdpgnfR.js +11 -0
  275. package/static/dashboard/assets/{NetworkPage-DV2V0qWO.js → NetworkPage-BAIPw-D_.js} +2 -2
  276. package/static/dashboard/assets/{PaneListHeader-D0c1yCFH.js → PaneListHeader-BBCMei0r.js} +1 -1
  277. package/static/dashboard/assets/SearchIndexingPanel-Ck4Y-xjR.js +1 -0
  278. package/static/dashboard/assets/{StatusSubjectPanel-oPSECj7i.js → StatusSubjectPanel-D1KWfAXE.js} +1 -1
  279. package/static/dashboard/assets/{StatusWorkspace-PdovQPKa.js → StatusWorkspace-JbA5gzXx.js} +1 -1
  280. package/static/dashboard/assets/{SystemSettingsPage-CtNDklkx.js → SystemSettingsPage-BbwQWsSR.js} +1 -1
  281. package/static/{settings/assets/SystemSettingsSubjectPanel-DExjDo_v.js → dashboard/assets/SystemSettingsSubjectPanel-N117nMt5.js} +5 -5
  282. package/static/dashboard/assets/XpodAiConnectionsPodStore-CdDOb4SV.js +18 -0
  283. package/static/dashboard/assets/{activity-B9ylSoyr.js → activity-DX1I96o_.js} +1 -1
  284. package/static/dashboard/assets/ai-connections-DwCRqOQa.js +1 -0
  285. package/static/dashboard/assets/{badge-BNbFbpIb.js → badge-k2vhtDqH.js} +1 -1
  286. package/static/dashboard/assets/collection-runtime-BFQ8teQJ.js +1 -0
  287. package/static/dashboard/assets/{dashboard-CD-FFVRD.js → dashboard-DWZ-fmCT.js} +4 -4
  288. package/static/dashboard/assets/dashboard-rk080mvG.css +1 -0
  289. package/static/dashboard/assets/dialog-1CCw-iHT.js +1 -0
  290. package/static/dashboard/assets/{download-nOP38LtW.js → download-C6-SNY1q.js} +1 -1
  291. package/static/dashboard/assets/{external-link-Bh2ewN9E.js → external-link-BgPcZ_j2.js} +1 -1
  292. package/static/dashboard/assets/index-6LcIUIYO.js +1 -0
  293. package/static/dashboard/assets/index-D7A8U6Pt.js +41 -0
  294. package/static/dashboard/assets/index-UD6QOE7H.js +4 -0
  295. package/static/dashboard/assets/{key-round-bGmguBLv.js → key-round-BOd7Jo0Q.js} +1 -1
  296. package/static/dashboard/assets/layout-DB3pMyTl.js +1 -0
  297. package/static/dashboard/assets/pod-collections-host-KnnGFZHL.js +8 -0
  298. package/static/dashboard/assets/{rotate-ccw-D2x__ODW.js → rotate-ccw-CCgJqsvx.js} +1 -1
  299. package/static/dashboard/assets/{skeleton-VOqft8WS.js → skeleton-Bc7abAn5.js} +1 -1
  300. package/static/dashboard/assets/{waypoints-fgRqm4oH.js → waypoints-s-opmJxx.js} +1 -1
  301. package/static/dashboard/assets/{workspace-layout-DV4dgeHX.js → workspace-layout-DqIQeTBV.js} +1 -1
  302. package/static/dashboard/dashboard.html +2 -2
  303. package/static/settings/assets/AiConfigContext-BB9KZySa.js +1 -0
  304. package/static/settings/assets/AiConfigPage-DGSXUkLs.js +1 -0
  305. package/static/settings/assets/DocumentProcessingPanel-Da2Re6Rg.js +1 -0
  306. package/static/settings/assets/IndexLifecyclePanel-BSZuIzgr.js +1 -0
  307. package/static/settings/assets/{IndexSubjectPanel-BgbI9c-a.js → IndexSubjectPanel-CFd0ay-r.js} +1 -1
  308. package/static/settings/assets/ModelAssignmentsPanel-CSAryvWJ.js +1 -0
  309. package/static/settings/assets/ModelsPage-BQwCwLHU.js +11 -0
  310. package/static/settings/assets/{NetworkPage-C8OA9Mtp.js → NetworkPage-BKymvsQc.js} +2 -2
  311. package/static/settings/assets/{PaneListHeader-COvlPJWj.js → PaneListHeader-Bjh3hRdQ.js} +1 -1
  312. package/static/settings/assets/SearchIndexingPanel-MjKL5QLU.js +1 -0
  313. package/static/settings/assets/{StatusSubjectPanel-BuLml7qn.js → StatusSubjectPanel-Bvl6K4mx.js} +1 -1
  314. package/static/settings/assets/StatusWorkspace-B0NzrP--.js +1 -0
  315. package/static/settings/assets/{SystemSettingsPage-DlzmaWV-.js → SystemSettingsPage-D2z6Z4FE.js} +1 -1
  316. package/static/{auth-callback/assets/SystemSettingsSubjectPanel-CDqRuC4t.js → settings/assets/SystemSettingsSubjectPanel-4sxma4X4.js} +5 -5
  317. package/static/settings/assets/XpodAiConnectionsPodStore-BkNQ8fZx.js +3 -0
  318. package/static/settings/assets/{activity-TLerXgMs.js → activity-CgJJGpjx.js} +1 -1
  319. package/static/settings/assets/ai-connections-CryuHGKW.js +1 -0
  320. package/static/settings/assets/{badge-Bcs8iWrH.js → badge-C4NypVyT.js} +1 -1
  321. package/static/settings/assets/collection-runtime-CTLCDxAi.js +1 -0
  322. package/static/settings/assets/dialog-Csq0vWUi.js +1 -0
  323. package/static/settings/assets/{download-Ct3QmHPt.js → download-DaLJJ-Wp.js} +1 -1
  324. package/static/settings/assets/{external-link-C8KiHbss.js → external-link--w_iW5Au.js} +1 -1
  325. package/static/settings/assets/index-B0mQa59b.js +4 -0
  326. package/static/settings/assets/index-BukHiu4y.js +1 -0
  327. package/static/settings/assets/index-COmVnZi2.js +41 -0
  328. package/static/settings/assets/{index-browser-CazX1pc2.js → index-browser-BGpvHwD0.js} +1 -1
  329. package/static/settings/assets/{key-round-B19YD1GA.js → key-round-Bk4YRjBu.js} +1 -1
  330. package/static/settings/assets/layout-DB3pMyTl.js +1 -0
  331. package/static/settings/assets/pod-collections-host-KnnGFZHL.js +8 -0
  332. package/static/settings/assets/{rotate-ccw-AQm2kMgU.js → rotate-ccw-TCabbqsr.js} +1 -1
  333. package/static/settings/assets/{settings-C-VBtt9r.js → settings-CJWpdbNC.js} +5 -5
  334. package/static/settings/assets/settings-rk080mvG.css +1 -0
  335. package/static/settings/assets/{skeleton-DyZ1OBYf.js → skeleton-JDHYVe7t.js} +1 -1
  336. package/static/settings/assets/{waypoints-CC3wq-SU.js → waypoints-DDvEl-MU.js} +1 -1
  337. package/static/settings/assets/{workspace-layout-BxXNKgf9.js → workspace-layout-DV5QU30U.js} +1 -1
  338. package/static/settings/settings.html +2 -2
  339. package/static/auth-callback/assets/AiConfigContext-DqWRSm7n.js +0 -1
  340. package/static/auth-callback/assets/AiConfigPage-C5YYyvzl.js +0 -1
  341. package/static/auth-callback/assets/DocumentProcessingPanel-DZCiacBz.js +0 -1
  342. package/static/auth-callback/assets/IndexLifecyclePanel-CCLeOljB.js +0 -1
  343. package/static/auth-callback/assets/ModelAssignmentsPanel-jUUTDKuh.js +0 -1
  344. package/static/auth-callback/assets/ModelsPage-B08zdtff.js +0 -10
  345. package/static/auth-callback/assets/SearchIndexingPanel-D4YRg1-8.js +0 -1
  346. package/static/auth-callback/assets/StatusWorkspace-Cp5LQ9Yx.js +0 -1
  347. package/static/auth-callback/assets/ai-connections-CHNTKemd.js +0 -1
  348. package/static/auth-callback/assets/auth-callback-DWMMiFjh.css +0 -1
  349. package/static/auth-callback/assets/index-DSNnLUbU.js +0 -4
  350. package/static/auth-callback/assets/index-FHhHoKgp.js +0 -41
  351. package/static/dashboard/assets/AiConfigContext-BpPDKSzI.js +0 -1
  352. package/static/dashboard/assets/AiConfigPage-wsqEZDIX.js +0 -1
  353. package/static/dashboard/assets/DocumentProcessingPanel-CDSy3juc.js +0 -1
  354. package/static/dashboard/assets/IndexLifecyclePanel-CVd6vQCX.js +0 -1
  355. package/static/dashboard/assets/ModelAssignmentsPanel-BeBZhnQd.js +0 -1
  356. package/static/dashboard/assets/ModelsPage-B6McSCJw.js +0 -25
  357. package/static/dashboard/assets/SearchIndexingPanel-CGs0OZ12.js +0 -1
  358. package/static/dashboard/assets/ai-connections-CHNTKemd.js +0 -1
  359. package/static/dashboard/assets/dashboard-DWMMiFjh.css +0 -1
  360. package/static/dashboard/assets/index-PgS-Bt7X.js +0 -41
  361. package/static/dashboard/assets/index-QN2QgEwr.js +0 -4
  362. package/static/settings/assets/AiConfigContext-zKQYdfai.js +0 -1
  363. package/static/settings/assets/AiConfigPage-Bs1EE3Ei.js +0 -1
  364. package/static/settings/assets/DocumentProcessingPanel-CSFfdOXS.js +0 -1
  365. package/static/settings/assets/IndexLifecyclePanel-BR9Su49B.js +0 -1
  366. package/static/settings/assets/ModelAssignmentsPanel-C0BE7nbk.js +0 -1
  367. package/static/settings/assets/ModelsPage-CU-YQ8_7.js +0 -10
  368. package/static/settings/assets/SearchIndexingPanel-Bdd46mIV.js +0 -1
  369. package/static/settings/assets/ai-connections-CHNTKemd.js +0 -1
  370. package/static/settings/assets/index-1Q2cRjZd.js +0 -41
  371. package/static/settings/assets/index-DBO_Vq0P.js +0 -4
  372. package/static/settings/assets/settings-DWMMiFjh.css +0 -1
@@ -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', browserMode: 'browserAssistedApiKey', browserLabel: '登录', description: 'OpenAI 模型与编码能力', homeUrl: 'https://openai.com', apiKeyUrl: 'https://platform.openai.com/api-keys', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://api.openai.com/v1' },
5
- { id: 'anthropic', name: 'Anthropic', browserMode: 'browserAssistedApiKey', browserLabel: '登录', description: 'Claude 模型与编码能力', homeUrl: 'https://www.anthropic.com', apiKeyUrl: 'https://console.anthropic.com/settings/keys', apiKeyPlaceholder: 'sk-ant-...', defaultBaseUrl: 'https://api.anthropic.com' },
6
- { id: 'kimi', name: 'Kimi', browserMode: 'browserAssistedApiKey', browserLabel: '登录', description: 'Moonshot AI 模型服务', homeUrl: 'https://www.moonshot.cn', apiKeyUrl: 'https://platform.moonshot.cn/console/api-keys', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://api.moonshot.cn/v1' },
7
- { id: 'bailian', name: '百炼', browserMode: 'browserAssistedApiKey', browserLabel: '登录', 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' },
8
- { id: 'deepseek', name: 'DeepSeek', browserMode: 'connectUnsupported', browserLabel: '不支持登录', description: 'DeepSeek 模型服务', homeUrl: 'https://www.deepseek.com', apiKeyUrl: 'https://platform.deepseek.com/api_keys', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://api.deepseek.com/v1' },
9
- { id: 'zhipu', name: '智谱 AI', browserMode: 'browserAssistedApiKey', browserLabel: '登录', description: '智谱 AI / GLM 模型服务', homeUrl: 'https://open.bigmodel.cn', apiKeyUrl: 'https://open.bigmodel.cn/usercenter/apikeys', apiKeyPlaceholder: 'id.secret-...', defaultBaseUrl: 'https://open.bigmodel.cn/api/paas/v4' },
10
- { id: 'ollama', name: 'Ollama', browserMode: 'connectUnsupported', browserLabel: '本地服务', description: '本地 Ollama 模型服务', homeUrl: 'https://ollama.com', defaultBaseUrl: 'http://localhost:11434/v1' },
11
- { id: 'custom', name: 'Custom', browserMode: 'browserAssistedApiKey', browserLabel: '配置', description: 'OpenAI / Anthropic 兼容的自定义模型服务', homeUrl: 'https://undefineds.co', apiKeyPlaceholder: 'sk-...', defaultBaseUrl: 'https://example.com/v1' },
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: 'API Keys', title: 'API KEYS' },
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
- return {
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
- ...(authorizationMethods.length ? {
668
- authorizationMethods,
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: 'border-emerald-500/40 bg-emerald-500/5', dot: 'bg-emerald-500' };
46
+ return { row: 'bg-emerald-500/5', dot: 'bg-emerald-500' };
42
47
  }
43
48
  if (health === 'unknown') {
44
- return { row: 'border-border/50 bg-background', dot: 'bg-muted-foreground/50' };
49
+ return { row: 'bg-background', dot: 'bg-muted-foreground/50' };
45
50
  }
46
- return { row: 'border-destructive/40 bg-destructive/5', dot: 'bg-destructive' };
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
+ }