@undefineds.co/xpod 0.4.3 → 0.4.5

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 (223) hide show
  1. package/config/cloud.json +1 -1
  2. package/config/main.json +36 -2
  3. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +3 -0
  4. package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +1 -1
  5. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +2 -1
  6. package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +101 -32
  7. package/dist/api/ai-gateway/connect/DeviceCodeProtocol.d.ts +80 -0
  8. package/dist/api/ai-gateway/connect/DeviceCodeProtocol.js +3 -0
  9. package/dist/api/ai-gateway/connect/FileSessionImportAdapter.d.ts +38 -0
  10. package/dist/api/ai-gateway/connect/FileSessionImportAdapter.js +63 -0
  11. package/dist/api/ai-gateway/connect/LoopbackAuthorizationCallbackReceiver.d.ts +24 -0
  12. package/dist/api/ai-gateway/connect/LoopbackAuthorizationCallbackReceiver.js +136 -0
  13. package/dist/api/ai-gateway/connect/OAuthConnectAdapter.d.ts +2 -0
  14. package/dist/api/ai-gateway/connect/OAuthConnectAdapter.js +2 -1
  15. package/dist/api/ai-gateway/connect/OAuthIntegrationRegistry.d.ts +17 -13
  16. package/dist/api/ai-gateway/connect/OAuthIntegrationRegistry.js +52 -19
  17. package/dist/api/ai-gateway/connect/OpenAiSubscriptionSessionImportAdapter.d.ts +4 -28
  18. package/dist/api/ai-gateway/connect/OpenAiSubscriptionSessionImportAdapter.js +7 -58
  19. package/dist/api/ai-gateway/connect/ProviderAuthorizationProfiles.d.ts +4 -0
  20. package/dist/api/ai-gateway/connect/ProviderAuthorizationProfiles.js +116 -0
  21. package/dist/api/ai-gateway/connect/SessionImportProfiles.d.ts +10 -0
  22. package/dist/api/ai-gateway/connect/SessionImportProfiles.js +112 -0
  23. package/dist/api/ai-gateway/connect/index.d.ts +125 -23
  24. package/dist/api/ai-gateway/connect/index.js +1277 -260
  25. package/dist/api/ai-gateway/models/PodModelSelectionRepository.d.ts +4 -1
  26. package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +154 -19
  27. package/dist/api/ai-gateway/providers/DeepSeekRuntimeAdapter.js +10 -6
  28. package/dist/api/ai-gateway/providers/ModelsDevCatalog.d.ts +7 -0
  29. package/dist/api/ai-gateway/providers/ModelsDevCatalog.js +25 -1
  30. package/dist/api/ai-gateway/providers/OfferingAuthorization.d.ts +24 -0
  31. package/dist/api/ai-gateway/providers/OfferingAuthorization.js +37 -0
  32. package/dist/api/ai-gateway/providers/OpenAiRuntimeAdapter.d.ts +1 -1
  33. package/dist/api/ai-gateway/providers/OpenAiRuntimeAdapter.js +5 -3
  34. package/dist/api/ai-gateway/providers/ProviderRegistry.d.ts +6 -3
  35. package/dist/api/ai-gateway/providers/ProviderRegistry.js +55 -16
  36. package/dist/api/ai-gateway/providers/ProviderRuntimeAdapter.d.ts +2 -0
  37. package/dist/api/ai-gateway/providers/ProviderRuntimeAdapter.js +37 -4
  38. package/dist/api/ai-gateway/providers/ProviderRuntimeRegistry.js +7 -0
  39. package/dist/api/ai-gateway/quota/SubscriptionQuotaAdapters.js +2 -1
  40. package/dist/api/ai-gateway/routing/ModelRouter.js +3 -1
  41. package/dist/api/container/common.js +30 -40
  42. package/dist/api/container/index.js +6 -2
  43. package/dist/api/container/routes.js +5 -22
  44. package/dist/api/handlers/AiClientConfigurationHandler.js +39 -1
  45. package/dist/api/handlers/AiGatewayManagementHandler.d.ts +3 -0
  46. package/dist/api/handlers/AiGatewayManagementHandler.js +173 -58
  47. package/dist/api/service/AiClientConfigurationService.d.ts +4 -15
  48. package/dist/api/service/AiClientConfigurationService.js +17 -64
  49. package/dist/cli/commands/start.js +2 -0
  50. package/dist/components/components.jsonld +1 -0
  51. package/dist/components/context.jsonld +80 -0
  52. package/dist/http/InternalPodDataHttpHandler.d.ts +3 -0
  53. package/dist/http/InternalPodDataHttpHandler.js +13 -2
  54. package/dist/http/InternalPodDataHttpHandler.jsonld +12 -0
  55. package/dist/identity/oidc/RememberedClientPromptFactory.d.ts +1 -1
  56. package/dist/identity/oidc/RememberedClientPromptFactory.js +5 -2
  57. package/dist/identity/oidc/RememberedConsentHandler.js +1 -1
  58. package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +1 -0
  59. package/dist/identity/oidc/ScopedPickWebIdHandler.js +26 -0
  60. package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +4 -0
  61. package/dist/identity/oidc/SessionBoundIdentityProviderFactory.d.ts +10 -0
  62. package/dist/identity/oidc/SessionBoundIdentityProviderFactory.js +21 -0
  63. package/dist/identity/oidc/SessionBoundIdentityProviderFactory.jsonld +172 -0
  64. package/dist/index.d.ts +2 -1
  65. package/dist/index.js +4 -2
  66. package/dist/runtime/component-parameter-keys.js +18 -2
  67. package/dist/storage/accessors/MixDataAccessor.js +6 -2
  68. package/dist/storage/rdf/PostgresRdfEngine.js +10 -2
  69. package/dist/storage/rdf/RdfContentTypes.js +4 -0
  70. package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.d.ts +3 -0
  71. package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.js +13 -16
  72. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsList.js +1 -3
  73. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.d.ts +2 -1
  74. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +2 -2
  75. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.d.ts +4 -1
  76. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.js +127 -57
  77. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.d.ts +6 -4
  78. package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +436 -83
  79. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.d.ts +3 -2
  80. package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +212 -76
  81. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.d.ts +6 -4
  82. package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.js +58 -41
  83. package/node_modules/@undefineds.co/ai-connections/dist/AiQuotaCard.d.ts +4 -1
  84. package/node_modules/@undefineds.co/ai-connections/dist/AiQuotaCard.js +63 -4
  85. package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.d.ts +46 -6
  86. package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.js +143 -14
  87. package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.d.ts +19 -3
  88. package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.js +45 -7
  89. package/node_modules/@undefineds.co/ai-connections/dist/client-config/codex.d.ts +7 -1
  90. package/node_modules/@undefineds.co/ai-connections/dist/client-config/codex.js +305 -40
  91. package/node_modules/@undefineds.co/ai-connections/dist/client-config/index.cjs +383 -50
  92. package/node_modules/@undefineds.co/ai-connections/dist/client-config/json-env-adapter.js +3 -2
  93. package/node_modules/@undefineds.co/ai-connections/dist/client-config/pi.js +12 -3
  94. package/node_modules/@undefineds.co/ai-connections/dist/client-config/transaction.d.ts +2 -0
  95. package/node_modules/@undefineds.co/ai-connections/dist/client-config/transaction.js +39 -4
  96. package/node_modules/@undefineds.co/ai-connections/dist/client-config/types.d.ts +8 -1
  97. package/node_modules/@undefineds.co/ai-connections/dist/client-visuals.d.ts +2 -0
  98. package/node_modules/@undefineds.co/ai-connections/dist/client-visuals.js +14 -0
  99. package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +2 -2
  100. package/node_modules/@undefineds.co/ai-connections/dist/controller.js +216 -57
  101. package/node_modules/@undefineds.co/ai-connections/dist/index.d.ts +3 -1
  102. package/node_modules/@undefineds.co/ai-connections/dist/index.js +16 -2
  103. package/node_modules/@undefineds.co/ai-connections/dist/provider-visuals.js +6 -1
  104. package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-executor.js +27 -6
  105. package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.js +1 -1
  106. package/node_modules/@undefineds.co/drizzle-solid/dist/core/query-builders/select-query-builder.js +2 -2
  107. package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/update-builder.js +12 -1
  108. package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/builder.js +1 -1
  109. package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/array.js +8 -0
  110. package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/default.js +9 -1
  111. package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/inline.js +3 -3
  112. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-executor.js +27 -6
  113. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.js +1 -1
  114. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/query-builders/select-query-builder.js +2 -2
  115. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/update-builder.js +12 -1
  116. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/builder.js +1 -1
  117. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/array.js +8 -0
  118. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/default.js +9 -1
  119. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/inline.js +3 -3
  120. package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +31 -0
  121. package/package.json +2 -2
  122. package/static/app/assets/{index-BikOyGPu.js → index-BCZVzMpj.js} +44 -44
  123. package/static/app/assets/inrupt-smoke.js +2 -2
  124. package/static/app/assets/main.css +1 -1
  125. package/static/app/assets/main.js +10 -10
  126. package/static/auth-callback/assets/{AiConfigContext-DrCH6ckh.js → AiConfigContext-DVkh8Kg2.js} +1 -1
  127. package/static/auth-callback/assets/{AiConfigPage-DVrlSSdA.js → AiConfigPage-HWVWpe-P.js} +1 -1
  128. package/static/auth-callback/assets/{DocumentProcessingPanel-_Md-v2R2.js → DocumentProcessingPanel-DDq-mH17.js} +1 -1
  129. package/static/auth-callback/assets/{IndexLifecyclePanel-BbWwe23H.js → IndexLifecyclePanel-Cx4cjrdP.js} +1 -1
  130. package/static/auth-callback/assets/{IndexSubjectPanel-upH9MjU-.js → IndexSubjectPanel-DzSw8M9j.js} +1 -1
  131. package/static/auth-callback/assets/{ModelAssignmentsPanel-CVGH4t4g.js → ModelAssignmentsPanel-BsRxGc2p.js} +1 -1
  132. package/static/auth-callback/assets/ModelsPage-DEe1lo6Q.js +10 -0
  133. package/static/auth-callback/assets/{NetworkPage-Dcbrb1fd.js → NetworkPage-DBiEh1Oz.js} +1 -1
  134. package/static/auth-callback/assets/{PaneListHeader-DrY-1wBq.js → PaneListHeader-lEEgjyN9.js} +1 -1
  135. package/static/auth-callback/assets/{SearchIndexingPanel-Dw25y53v.js → SearchIndexingPanel-CZCUUGt6.js} +1 -1
  136. package/static/auth-callback/assets/{StatusSubjectPanel-BHlz9rNF.js → StatusSubjectPanel-21qdueMr.js} +1 -1
  137. package/static/auth-callback/assets/{StatusWorkspace-BH7exQP5.js → StatusWorkspace-R8RKsqVz.js} +1 -1
  138. package/static/auth-callback/assets/{SystemSettingsPage-BYLKnM9f.js → SystemSettingsPage-BQ4e-4Wy.js} +1 -1
  139. package/static/auth-callback/assets/{SystemSettingsSubjectPanel-DT34LKku.js → SystemSettingsSubjectPanel-BopXD7SE.js} +1 -1
  140. package/static/auth-callback/assets/{activity-DdULRcSI.js → activity-DSfmTGfY.js} +1 -1
  141. package/static/auth-callback/assets/ai-connections-C03UWyPh.js +1 -0
  142. package/static/auth-callback/assets/{auth-callback-D7l2P25I.js → auth-callback-DleaitAS.js} +91 -91
  143. package/static/auth-callback/assets/auth-callback-vPSC2d2F.css +1 -0
  144. package/static/auth-callback/assets/{badge-j4zjFnmg.js → badge-B0aLu1sl.js} +1 -1
  145. package/static/auth-callback/assets/{download-DGXoks01.js → download-D_bFwdYJ.js} +1 -1
  146. package/static/auth-callback/assets/{external-link-CvbgXXaI.js → external-link-DRfjTaQu.js} +1 -1
  147. package/static/auth-callback/assets/{index-LRWvKJ2i.js → index-CG23cLYQ.js} +1 -1
  148. package/static/auth-callback/assets/{index-BOK3j1dz.js → index-CyV9ejwx.js} +1 -1
  149. package/static/auth-callback/assets/{index-browser-C1Qpe0g4.js → index-browser-D70paxIg.js} +1 -1
  150. package/static/auth-callback/assets/{key-round-CmgS5UdQ.js → key-round-BjV_mVYH.js} +1 -1
  151. package/static/auth-callback/assets/{rotate-ccw-c4CSG6Rq.js → rotate-ccw-CKoOAfse.js} +1 -1
  152. package/static/auth-callback/assets/{skeleton-ZPMTiRSM.js → skeleton-BZNjZaHx.js} +1 -1
  153. package/static/auth-callback/assets/{waypoints-xjyBflU5.js → waypoints-DoaJVzZv.js} +1 -1
  154. package/static/auth-callback/assets/{workspace-layout-B9nj6F5J.js → workspace-layout-CaP46_ZR.js} +1 -1
  155. package/static/auth-callback/auth-callback.html +2 -2
  156. package/static/dashboard/assets/{AiConfigContext-BWOOSYNr.js → AiConfigContext-BSrR27yD.js} +1 -1
  157. package/static/dashboard/assets/{AiConfigPage-CG2kQ9Wy.js → AiConfigPage-D65Z2LET.js} +1 -1
  158. package/static/dashboard/assets/{DocumentProcessingPanel-B4VXajfj.js → DocumentProcessingPanel-DyTe7Nei.js} +1 -1
  159. package/static/dashboard/assets/{IndexLifecyclePanel-CWCy0CC8.js → IndexLifecyclePanel-fK8ZGlrL.js} +1 -1
  160. package/static/dashboard/assets/{IndexSubjectPanel-CSgyQnG_.js → IndexSubjectPanel-CZPGor7v.js} +1 -1
  161. package/static/dashboard/assets/{ModelAssignmentsPanel-CdGIGidT.js → ModelAssignmentsPanel-CfYn5LMQ.js} +1 -1
  162. package/static/dashboard/assets/ModelsPage-djlJsghe.js +25 -0
  163. package/static/dashboard/assets/{NetworkPage-COia9NCH.js → NetworkPage-Coj6GnPl.js} +1 -1
  164. package/static/dashboard/assets/{PaneListHeader-SZWyr602.js → PaneListHeader-CUuFNSsZ.js} +1 -1
  165. package/static/dashboard/assets/{SearchIndexingPanel-D6PZtG6k.js → SearchIndexingPanel-CyJLioXS.js} +1 -1
  166. package/static/dashboard/assets/{StatusSubjectPanel-CxRD4kv0.js → StatusSubjectPanel-Cv26GSVC.js} +1 -1
  167. package/static/dashboard/assets/{StatusWorkspace-D3PfmY9-.js → StatusWorkspace-CDjFrsmB.js} +1 -1
  168. package/static/dashboard/assets/{SystemSettingsPage-COg1MmMj.js → SystemSettingsPage-BuNOyNoa.js} +1 -1
  169. package/static/dashboard/assets/{SystemSettingsSubjectPanel-BSVVB9Ol.js → SystemSettingsSubjectPanel-B2dasdez.js} +1 -1
  170. package/static/dashboard/assets/{activity-BJJx5cN-.js → activity-D1d2j_4Z.js} +1 -1
  171. package/static/dashboard/assets/ai-connections-C03UWyPh.js +1 -0
  172. package/static/dashboard/assets/{badge-elmrFSCj.js → badge-BeAAv6sp.js} +1 -1
  173. package/static/dashboard/assets/{dashboard-IvQrNmru.js → dashboard-BnGrUGv8.js} +68 -68
  174. package/static/dashboard/assets/dashboard-vPSC2d2F.css +1 -0
  175. package/static/dashboard/assets/{download-C3FH2KzG.js → download-DfFGiZRn.js} +1 -1
  176. package/static/dashboard/assets/{external-link-Bx7F0uKb.js → external-link-IsjU_LC9.js} +1 -1
  177. package/static/dashboard/assets/{index-liRoRIP1.js → index-BWQTucPe.js} +1 -1
  178. package/static/dashboard/assets/{index-M4-kWiGz.js → index-Dj75OjnW.js} +1 -1
  179. package/static/dashboard/assets/{key-round-CXg0bKAj.js → key-round-BJ_9wmQ_.js} +1 -1
  180. package/static/dashboard/assets/{rotate-ccw-yxJKOBrX.js → rotate-ccw-D3JGiFbR.js} +1 -1
  181. package/static/dashboard/assets/{skeleton-x9jNGrQT.js → skeleton-CC4nc-z-.js} +1 -1
  182. package/static/dashboard/assets/{waypoints-CTKeRsGp.js → waypoints-h7sqFqiG.js} +1 -1
  183. package/static/dashboard/assets/{workspace-layout--YkgkS38.js → workspace-layout-ZAPTOHhs.js} +1 -1
  184. package/static/dashboard/dashboard.html +2 -2
  185. package/static/settings/assets/{AiConfigContext-CelxXFcQ.js → AiConfigContext-lGkWpvKP.js} +1 -1
  186. package/static/settings/assets/{AiConfigPage-BaP-EovM.js → AiConfigPage-BbZWIBZq.js} +1 -1
  187. package/static/settings/assets/{DocumentProcessingPanel-DzLZluFv.js → DocumentProcessingPanel-DvYdjT-V.js} +1 -1
  188. package/static/settings/assets/{IndexLifecyclePanel-Bzj1-_oI.js → IndexLifecyclePanel-W5OGQZj7.js} +1 -1
  189. package/static/settings/assets/{IndexSubjectPanel-B263QLUh.js → IndexSubjectPanel-g_Aa5soq.js} +1 -1
  190. package/static/settings/assets/{ModelAssignmentsPanel-BAdT4UR0.js → ModelAssignmentsPanel-COlMBKXj.js} +1 -1
  191. package/static/settings/assets/ModelsPage-mApKi-aw.js +10 -0
  192. package/static/settings/assets/{NetworkPage-BK6a7l4g.js → NetworkPage-BDT05JUq.js} +1 -1
  193. package/static/settings/assets/{PaneListHeader-BAS2ICPq.js → PaneListHeader-CByhNpMG.js} +1 -1
  194. package/static/settings/assets/{SearchIndexingPanel-Cb_p-NgW.js → SearchIndexingPanel-BFAyzXls.js} +1 -1
  195. package/static/settings/assets/{StatusSubjectPanel-CcjqUua6.js → StatusSubjectPanel-Bz_XZ3VU.js} +1 -1
  196. package/static/settings/assets/{StatusWorkspace-7Ibty3_k.js → StatusWorkspace-CeVypHcW.js} +1 -1
  197. package/static/settings/assets/{SystemSettingsPage-DPSz4dg4.js → SystemSettingsPage-Dh7cQIfx.js} +1 -1
  198. package/static/settings/assets/{SystemSettingsSubjectPanel-Cb4HkKT5.js → SystemSettingsSubjectPanel-CSnpciO0.js} +1 -1
  199. package/static/settings/assets/{activity-Qhsr5Pxu.js → activity-DXz3EfFN.js} +1 -1
  200. package/static/settings/assets/ai-connections-C03UWyPh.js +1 -0
  201. package/static/settings/assets/{badge-B1vf2qCl.js → badge-1ZA144i4.js} +1 -1
  202. package/static/settings/assets/{download-CYNt0aZ-.js → download-CUHimlYa.js} +1 -1
  203. package/static/settings/assets/{external-link-ClKb39NT.js → external-link-BYHSVj2Z.js} +1 -1
  204. package/static/settings/assets/{index-y8ew3MLM.js → index-DKPDMdW5.js} +1 -1
  205. package/static/settings/assets/{index-browser-DJhvgpNI.js → index-browser-BaEFz0e0.js} +1 -1
  206. package/static/settings/assets/{index-BgiIDSch.js → index-viSgSvbO.js} +1 -1
  207. package/static/settings/assets/{key-round-5d-DrJz-.js → key-round-COByPOg8.js} +1 -1
  208. package/static/settings/assets/{rotate-ccw-CHZY7kS5.js → rotate-ccw-BC2EN6cE.js} +1 -1
  209. package/static/settings/assets/{settings-D5VRWyx6.js → settings-DLH9XUoT.js} +86 -86
  210. package/static/settings/assets/settings-vPSC2d2F.css +1 -0
  211. package/static/settings/assets/{skeleton-FNabNxNE.js → skeleton-B0-FOhKL.js} +1 -1
  212. package/static/settings/assets/{waypoints-BnL_8ljo.js → waypoints-DGMsvqWo.js} +1 -1
  213. package/static/settings/assets/{workspace-layout-D5q3F59S.js → workspace-layout-B0SVrJIu.js} +1 -1
  214. package/static/settings/settings.html +2 -2
  215. package/static/auth-callback/assets/ModelsPage-BpkVFKbf.js +0 -10
  216. package/static/auth-callback/assets/ai-connections-Bm52UO7Y.js +0 -1
  217. package/static/auth-callback/assets/auth-callback-01yz-UDU.css +0 -1
  218. package/static/dashboard/assets/ModelsPage-Chd5UK0D.js +0 -25
  219. package/static/dashboard/assets/ai-connections-Bm52UO7Y.js +0 -1
  220. package/static/dashboard/assets/dashboard-01yz-UDU.css +0 -1
  221. package/static/settings/assets/ModelsPage-C0NFa1zq.js +0 -10
  222. package/static/settings/assets/ai-connections-Bm52UO7Y.js +0 -1
  223. package/static/settings/assets/settings-01yz-UDU.css +0 -1
@@ -1,6 +1,7 @@
1
- import type { AiConnectionsClient } from './ai-connections-client';
1
+ import type { AiConnectionsClient, AiGatewayModel } from './ai-connections-client';
2
2
  import { type AiClientConfigurationBridge } from './AiClientConfigurationSection';
3
- export declare function AiGatewayKeysSection({ client, clientConfigurationBridge, }: {
3
+ export declare function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewayModels, }: {
4
4
  client: AiConnectionsClient;
5
5
  clientConfigurationBridge?: AiClientConfigurationBridge;
6
+ gatewayModels?: AiGatewayModel[];
6
7
  }): import("react").JSX.Element;
@@ -1,68 +1,178 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
3
- import { Badge, Button, Input, cn } from '@undefineds.co/shared-ui';
4
- import { Copy, KeyRound, Loader2, Play, Square, Trash2, } from 'lucide-react';
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { Button, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, cn, dismissToast, toast } from '@undefineds.co/shared-ui';
4
+ import { Check, ChevronDown, Copy, KeyRound, Loader2, Power, RefreshCw, Trash2, } from 'lucide-react';
5
5
  import { AI_CONNECTIONS_CLIENTS, AI_CLIENT_LABELS, AiClientIcon, manualConfigurationText, } from './AiClientConfigurationSection.js';
6
6
  const DEFAULT_KEY_NAME = '我的 API Key';
7
- export function AiGatewayKeysSection({ client, clientConfigurationBridge, }) {
7
+ export function AiGatewayKeysSection({ client, clientConfigurationBridge, gatewayModels, }) {
8
8
  const [keys, setKeys] = useState([]);
9
9
  const [loading, setLoading] = useState(true);
10
10
  const [name, setName] = useState(DEFAULT_KEY_NAME);
11
- const [target, setTarget] = useState('');
11
+ const [showCreate, setShowCreate] = useState(false);
12
+ const applicationVersions = useRef({});
13
+ const [openMenuKeyId, setOpenMenuKeyId] = useState();
14
+ const [applications, setApplications] = useState({});
15
+ const plaintexts = useRef(new Map());
16
+ const operation = useRef(false);
12
17
  const [creating, setCreating] = useState(false);
13
18
  const [busyKeyId, setBusyKeyId] = useState();
14
- const [newPlaintext, setNewPlaintext] = useState();
19
+ const [applying, setApplying] = useState();
15
20
  const [error, setError] = useState();
21
+ const [copied, setCopied] = useState({});
22
+ const copyTimers = useRef(new Map());
23
+ const copyGeneration = useRef(0);
24
+ const notification = useRef(undefined);
25
+ const notify = useCallback((options) => {
26
+ if (notification.current)
27
+ dismissToast(notification.current);
28
+ notification.current = toast(options);
29
+ }, []);
30
+ const notifyError = useCallback((cause) => {
31
+ notify({ variant: 'destructive', description: errorMessage(cause), duration: 8000 });
32
+ }, [notify]);
33
+ const clearCopyFeedback = (keyId) => {
34
+ clearTimeout(copyTimers.current.get(keyId));
35
+ copyTimers.current.delete(keyId);
36
+ setCopied((current) => {
37
+ const next = { ...current };
38
+ delete next[keyId];
39
+ return next;
40
+ });
41
+ };
16
42
  useEffect(() => {
17
43
  let active = true;
44
+ copyGeneration.current += 1;
45
+ const timers = copyTimers.current;
18
46
  setLoading(true);
19
47
  setError(undefined);
48
+ setApplications({});
49
+ plaintexts.current.clear();
50
+ const versions = { ...applicationVersions.current };
20
51
  void client.listGatewayKeys()
21
52
  .then((records) => {
22
- if (active)
23
- setKeys(records.filter((record) => !record.revokedAt));
53
+ if (!active)
54
+ return;
55
+ const visible = records.filter((record) => !record.revokedAt);
56
+ setKeys(visible);
57
+ if (!clientConfigurationBridge)
58
+ return;
59
+ void Promise.all(AI_CONNECTIONS_CLIENTS.map(async (clientId) => {
60
+ try {
61
+ const status = await clientConfigurationBridge.inspect(clientId);
62
+ if (!active || applicationVersions.current[clientId] !== versions[clientId] ||
63
+ status.status !== 'configured' || !status.appliedKeyFingerprint)
64
+ return;
65
+ const matches = visible.filter((record) => record.fingerprint === status.appliedKeyFingerprint);
66
+ if (matches.length !== 1)
67
+ return;
68
+ const keyId = matches[0].id;
69
+ setApplications((current) => ({ ...current, [clientId]: keyId }));
70
+ }
71
+ catch {
72
+ // Unavailable inspection must not infer ownership from historical metadata.
73
+ }
74
+ }));
24
75
  })
25
76
  .catch((cause) => {
26
77
  if (active)
27
- setError(errorMessage(cause));
78
+ notifyError(cause);
28
79
  })
29
80
  .finally(() => {
30
- if (active)
81
+ if (active) {
82
+ setCopied({});
83
+ setOpenMenuKeyId(undefined);
31
84
  setLoading(false);
85
+ }
32
86
  });
33
87
  return () => {
34
88
  active = false;
89
+ copyGeneration.current += 1;
90
+ timers.forEach(clearTimeout);
91
+ timers.clear();
92
+ if (notification.current)
93
+ dismissToast(notification.current);
35
94
  };
36
- }, [client]);
37
- const copyConfiguration = async (plaintext, selectedTarget = target) => {
38
- const content = selectedTarget
39
- ? manualConfigurationText(selectedTarget, client.apiBase, plaintext)
40
- : genericConfigurationText(client.apiBase, plaintext);
41
- if (!navigator.clipboard?.writeText) {
42
- throw new Error('当前浏览器无法访问剪贴板,请从下方复制新创建的 API Key。');
43
- }
44
- await navigator.clipboard.writeText(content);
45
- };
46
- const applyConfiguration = async (plaintext, selectedTarget) => {
47
- if (!clientConfigurationBridge) {
48
- await copyConfiguration(plaintext, selectedTarget);
95
+ }, [client, clientConfigurationBridge, notifyError]);
96
+ useEffect(() => {
97
+ if (!openMenuKeyId)
49
98
  return;
99
+ const closeOutside = (event) => {
100
+ const target = event.target;
101
+ if (target instanceof Element && target.closest('[data-client-menu]')?.getAttribute('data-client-menu') === openMenuKeyId)
102
+ return;
103
+ setOpenMenuKeyId(undefined);
104
+ };
105
+ document.addEventListener('pointerdown', closeOutside);
106
+ return () => document.removeEventListener('pointerdown', closeOutside);
107
+ }, [openMenuKeyId]);
108
+ const runKeyOperation = async (keyId, action) => {
109
+ if (operation.current)
110
+ return;
111
+ operation.current = true;
112
+ setBusyKeyId(keyId);
113
+ setError(undefined);
114
+ try {
115
+ await action();
116
+ }
117
+ catch (cause) {
118
+ notifyError(cause);
50
119
  }
51
- const plan = await clientConfigurationBridge.plan({ client: selectedTarget, endpoint: client.apiBase });
52
- if (plan.confirmation?.required) {
53
- await copyConfiguration(plaintext, selectedTarget);
54
- throw new Error(`${AI_CLIENT_LABELS[selectedTarget]} 需要确认后才能写入;配置已复制。`);
120
+ finally {
121
+ operation.current = false;
122
+ setBusyKeyId(undefined);
123
+ setApplying(undefined);
55
124
  }
125
+ };
126
+ const applyPlan = async (keyId, plan, refresh = false) => {
127
+ if (!clientConfigurationBridge)
128
+ return;
129
+ setApplying({ keyId, clientId: plan.client });
130
+ const plaintext = plaintexts.current.get(keyId) ?? await client.revealGatewayKey(keyId);
56
131
  await clientConfigurationBridge.apply({
57
- client: selectedTarget,
132
+ client: plan.client,
58
133
  planId: plan.planId,
59
134
  apiKey: plaintext,
135
+ ...(plan.confirmation?.required ? { confirmation: {
136
+ token: plan.confirmation.token,
137
+ targetHash: plan.confirmation.targetHash,
138
+ } } : {}),
60
139
  });
61
- const status = await clientConfigurationBridge.verify({ client: selectedTarget, planId: plan.planId });
62
- if (status.status !== 'configured') {
63
- throw new Error(status.message ?? `${AI_CLIENT_LABELS[selectedTarget]} 配置验证失败。`);
64
- }
140
+ setApplications((current) => ({ ...current, [plan.client]: keyId }));
141
+ notify({ variant: 'success', description: plan.client === 'codex' && gatewayModels === undefined
142
+ ? 'Codex 连接配置已应用,模型目录尚未加载,请稍后刷新配置。'
143
+ : `${AI_CLIENT_LABELS[plan.client]} ${refresh ? 'endpoint 已刷新。' : '配置已应用。'}` });
65
144
  };
145
+ const applyExistingConfiguration = (record, selectedTarget, refresh = false) => runKeyOperation(record.id, async () => {
146
+ if (!clientConfigurationBridge || (refresh && applications[selectedTarget] !== record.id))
147
+ return;
148
+ setApplying({ keyId: record.id, clientId: selectedTarget });
149
+ applicationVersions.current[selectedTarget] = (applicationVersions.current[selectedTarget] ?? 0) + 1;
150
+ const plan = await clientConfigurationBridge.plan({
151
+ client: selectedTarget,
152
+ endpoint: client.apiBase,
153
+ ...(selectedTarget === 'codex' && gatewayModels !== undefined ? {
154
+ activeModels: gatewayModels.map(({ id, provider, displayName, availability, contextWindow, inputModalities, capabilities }) => ({
155
+ id, provider, displayName, availability, contextWindow, inputModalities, capabilities,
156
+ })),
157
+ } : {}),
158
+ });
159
+ await applyPlan(record.id, plan, refresh);
160
+ });
161
+ const restore = (record, selectedTarget) => runKeyOperation(record.id, async () => {
162
+ if (!clientConfigurationBridge || applications[selectedTarget] !== record.id)
163
+ return;
164
+ applicationVersions.current[selectedTarget] = (applicationVersions.current[selectedTarget] ?? 0) + 1;
165
+ const status = await clientConfigurationBridge.restore(selectedTarget);
166
+ if (status.status !== 'notConfigured' && status.status !== 'failedAndRestored') {
167
+ throw new Error(status.message ?? '撤回未完成,请重试。');
168
+ }
169
+ setApplications((current) => {
170
+ const next = { ...current };
171
+ delete next[selectedTarget];
172
+ return next;
173
+ });
174
+ notify({ description: `${AI_CLIENT_LABELS[selectedTarget]} 配置已撤回,API Key 已保留。` });
175
+ });
66
176
  const create = async () => {
67
177
  const trimmedName = name.trim();
68
178
  if (!trimmedName) {
@@ -71,19 +181,15 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, }) {
71
181
  }
72
182
  setCreating(true);
73
183
  setError(undefined);
74
- setNewPlaintext(undefined);
75
184
  try {
76
185
  const created = await client.createGatewayKey({
77
186
  name: trimmedName,
78
- ...(target ? { appliedClient: target } : {}),
79
187
  });
80
188
  setKeys((current) => [created.record, ...current.filter((record) => record.id !== created.record.id)]);
81
- setNewPlaintext(created.plaintext);
189
+ plaintexts.current.set(created.record.id, created.plaintext);
190
+ notify({ variant: 'success', description: 'API Key 已创建,可在列表中复制或应用配置。' });
82
191
  setName(DEFAULT_KEY_NAME);
83
- if (target)
84
- await applyConfiguration(created.plaintext, target);
85
- else
86
- await copyConfiguration(created.plaintext);
192
+ setShowCreate(false);
87
193
  }
88
194
  catch (cause) {
89
195
  setError(errorMessage(cause));
@@ -100,58 +206,78 @@ export function AiGatewayKeysSection({ client, clientConfigurationBridge, }) {
100
206
  setKeys((current) => current.map((item) => item.id === record.id ? updated : item));
101
207
  }
102
208
  catch (cause) {
103
- setError(errorMessage(cause));
209
+ notifyError(cause);
104
210
  }
105
211
  finally {
106
212
  setBusyKeyId(undefined);
107
213
  }
108
214
  };
109
- const copyExistingConfiguration = async (record) => {
110
- setBusyKeyId(record.id);
111
- setError(undefined);
112
- try {
113
- const plaintext = await client.revealGatewayKey(record.id);
114
- const selectedTarget = asClientId(record.appliedClients?.[0]) ?? target;
115
- await copyConfiguration(plaintext, selectedTarget);
116
- }
117
- catch (cause) {
118
- setError(errorMessage(cause));
119
- }
120
- finally {
121
- setBusyKeyId(undefined);
215
+ const copyExistingConfiguration = (record, selectedTarget) => runKeyOperation(record.id, async () => {
216
+ const generation = copyGeneration.current;
217
+ const plaintext = plaintexts.current.get(record.id) ?? await client.revealGatewayKey(record.id);
218
+ if (generation !== copyGeneration.current)
219
+ return;
220
+ if (!navigator.clipboard?.writeText) {
221
+ throw new Error('当前浏览器无法访问剪贴板,请允许剪贴板访问后重试。');
122
222
  }
123
- };
223
+ await navigator.clipboard.writeText(selectedTarget === 'key'
224
+ ? plaintext
225
+ : manualConfigurationText(selectedTarget, client.apiBase, plaintext));
226
+ if (generation !== copyGeneration.current)
227
+ return;
228
+ clearTimeout(copyTimers.current.get(record.id));
229
+ setCopied((current) => ({ ...current, [record.id]: selectedTarget }));
230
+ copyTimers.current.set(record.id, setTimeout(() => clearCopyFeedback(record.id), 2000));
231
+ });
124
232
  const remove = async (record) => {
125
233
  setBusyKeyId(record.id);
126
234
  setError(undefined);
127
235
  try {
128
236
  await client.deleteGatewayKey(record.id);
237
+ plaintexts.current.delete(record.id);
238
+ clearCopyFeedback(record.id);
129
239
  setKeys((current) => current.filter((item) => item.id !== record.id));
130
240
  }
131
241
  catch (cause) {
132
- setError(errorMessage(cause));
242
+ notifyError(cause);
133
243
  }
134
244
  finally {
135
245
  setBusyKeyId(undefined);
136
246
  }
137
247
  };
138
- return (_jsxs("section", { className: "space-y-6", "aria-label": "API Keys", children: [_jsx("p", { className: "max-w-3xl text-sm text-muted-foreground", children: "API Key \u7528\u4E8E\u8BBF\u95EE Xpod Gateway\u3002Provider \u5BC6\u94A5\u4E0D\u4F1A\u5199\u5165\u5BA2\u6237\u7AEF\uFF1B\u5BA2\u6237\u7AEF\u53EA\u83B7\u5F97 Xpod \u5730\u5740\u548C\u8FD9\u91CC\u521B\u5EFA\u7684 Key\u3002" }), _jsxs("div", { className: "rounded-xl border border-border/70 p-4", children: [_jsxs("div", { className: "grid gap-3 lg:grid-cols-[minmax(14rem,1fr)_minmax(12rem,20rem)_auto] lg:items-end", children: [_jsxs("label", { className: "space-y-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "\u540D\u79F0" }), _jsx(Input, { "aria-label": "API Key \u540D\u79F0", value: name, onChange: (event) => setName(event.target.value) })] }), _jsxs("label", { className: "space-y-1.5", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "\u5E94\u7528\u5230\uFF08\u53EF\u9009\uFF09" }), _jsxs("select", { "aria-label": "\u5E94\u7528\u5230\u5BA2\u6237\u7AEF", value: target, onChange: (event) => setTarget(event.target.value), className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus-visible:border-ring", children: [_jsx("option", { value: "", children: "\u4EC5\u590D\u5236\u901A\u7528\u914D\u7F6E" }), AI_CONNECTIONS_CLIENTS.map((clientId) => (_jsx("option", { value: clientId, children: AI_CLIENT_LABELS[clientId] }, clientId)))] })] }), _jsxs(Button, { "aria-label": "\u521B\u5EFA API Key", className: "w-full whitespace-nowrap lg:w-auto", disabled: creating || !name.trim(), onClick: () => void create(), children: [creating ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(KeyRound, { className: "mr-2 h-4 w-4" }), target && clientConfigurationBridge ? '创建并应用配置' : '创建并复制配置'] })] }), target && !clientConfigurationBridge ? (_jsxs("p", { className: "mt-3 text-xs text-muted-foreground", children: ["\u5F53\u524D Web \u73AF\u5883\u4E0D\u652F\u6301\u81EA\u52A8\u5199\u5165\uFF0C\u5C06\u590D\u5236 ", AI_CLIENT_LABELS[target], " \u914D\u7F6E\u3002"] })) : null] }), newPlaintext ? (_jsxs("div", { "aria-label": "\u65B0\u521B\u5EFA\u7684 API Key", className: "rounded-lg border border-primary/25 bg-primary/5 p-4", children: [_jsx("p", { className: "text-sm font-medium", children: "API Key \u5DF2\u521B\u5EFA\uFF0C\u914D\u7F6E\u5DF2\u590D\u5236" }), _jsx("code", { className: "mt-2 block overflow-x-auto text-xs text-muted-foreground", children: newPlaintext })] })) : null, error ? (_jsx("p", { role: "alert", className: "rounded-md border border-destructive/30 px-3 py-2 text-sm text-destructive", children: error })) : null, _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 border-b border-border/60 pb-2", children: [_jsx("h2", { className: "text-sm font-medium", children: "\u5DF2\u521B\u5EFA" }), _jsxs("span", { className: "text-xs text-muted-foreground", children: [keys.length, " \u4E2A API Key"] })] }), loading ? (_jsxs("div", { className: "flex items-center gap-2 py-6 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "\u6B63\u5728\u8BFB\u53D6 API Keys"] })) : keys.length === 0 ? (_jsx("p", { className: "py-8 text-center text-sm text-muted-foreground", children: "\u5C1A\u672A\u521B\u5EFA API Key\u3002" })) : (_jsx("div", { className: "overflow-hidden rounded-xl border border-border/70", children: keys.map((record) => {
139
- const disabled = Boolean(record.disabledAt);
140
- const label = record.name || '未命名 API Key';
141
- const busy = busyKeyId === record.id;
142
- return (_jsxs("div", { "data-key-state": disabled ? 'disabled' : 'active', className: cn('grid min-h-16 grid-cols-[minmax(0,1fr)_auto] items-center gap-4 border-b border-border/60 px-4 py-3 last:border-b-0 md:grid-cols-[minmax(0,1.4fr)_minmax(10rem,.8fr)_auto]', disabled ? 'bg-muted/30 text-muted-foreground' : 'bg-emerald-500/[0.035]'), children: [_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx("span", { className: "truncate text-sm font-medium", children: label }), _jsx("code", { className: "shrink-0 text-xs text-muted-foreground", children: record.maskedHint ?? suffixHint(record.id) }), _jsx(Badge, { variant: "secondary", children: disabled ? '已停用' : '已启用' })] }), _jsx("div", { className: "hidden min-w-0 text-xs text-muted-foreground md:block", children: record.lastUsedAt ? `最后使用 ${formatTimestamp(record.lastUsedAt)}` : '暂无调用记录 · 尚未使用' }), _jsxs("div", { className: "flex items-center justify-end gap-1", children: [(record.appliedClients ?? []).map(asClientId).filter(isDefined).map((clientId) => (_jsx("span", { title: AI_CLIENT_LABELS[clientId], className: "flex h-8 w-8 items-center justify-center text-muted-foreground", children: _jsx(AiClientIcon, { client: clientId }) }, clientId))), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": `${disabled ? '启用' : '停用'} ${label}`, title: `${disabled ? '启用' : '停用'} ${label}`, disabled: busy, onClick: () => void setEnabled(record, disabled), children: disabled ? _jsx(Play, { className: "h-4 w-4" }) : _jsx(Square, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": `复制 ${label} 配置`, title: `复制 ${label} 配置`, disabled: busy, onClick: () => void copyExistingConfiguration(record), children: _jsx(Copy, { className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": `删除 ${label}`, title: `删除 ${label}`, disabled: busy, onClick: () => void remove(record), children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] }, record.id));
143
- }) }))] })] }));
144
- }
145
- function asClientId(value) {
146
- return value && AI_CONNECTIONS_CLIENTS.includes(value)
147
- ? value
148
- : undefined;
149
- }
150
- function genericConfigurationText(endpoint, apiKey) {
151
- return [
152
- `XPOD_BASE_URL=${endpoint.replace(/\/+$/u, '')}/v1`,
153
- `XPOD_API_KEY=${apiKey}`,
154
- ].join('\n');
248
+ return (_jsxs("section", { className: "[container-type:inline-size] space-y-6", "aria-label": "API Keys", children: [_jsx("p", { className: "max-w-3xl text-sm text-muted-foreground", children: "API Key \u7528\u4E8E\u8BBF\u95EE Xpod Gateway\u3002Provider \u5BC6\u94A5\u4E0D\u4F1A\u5199\u5165\u5BA2\u6237\u7AEF\uFF1B\u5BA2\u6237\u7AEF\u53EA\u83B7\u5F97 Xpod \u5730\u5740\u548C\u8FD9\u91CC\u521B\u5EFA\u7684 Key\u3002" }), _jsxs("div", { className: "flex items-center justify-between gap-3 border-b border-border/60 pb-2", children: [_jsxs("span", { className: "text-xs text-muted-foreground", children: [keys.length, " \u4E2A API Key"] }), _jsxs(Button, { size: "sm", "aria-label": "\u65B0\u5EFA API Key", disabled: creating || loading, onClick: () => { setError(undefined); setName(DEFAULT_KEY_NAME); setShowCreate(true); }, children: [_jsx(KeyRound, { className: "mr-2 h-4 w-4" }), "\u65B0\u5EFA API Key"] })] }), _jsx(Dialog, { open: showCreate, onOpenChange: (open) => { if (!creating)
249
+ setShowCreate(open); }, children: _jsxs(DialogContent, { className: "sm:max-w-md", "aria-describedby": undefined, children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "\u65B0\u5EFA API Key" }) }), _jsxs("form", { className: "space-y-5", onSubmit: (event) => { event.preventDefault(); if (!creating)
250
+ void create(); }, children: [_jsxs("label", { className: "block space-y-2", children: [_jsx("span", { className: "text-sm font-medium", children: "\u540D\u79F0" }), _jsx(Input, { autoFocus: true, "aria-label": "API Key \u540D\u79F0", value: name, disabled: creating, onChange: (event) => setName(event.target.value) })] }), error ? _jsx("p", { role: "alert", className: "text-sm text-destructive", children: error }) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", disabled: creating, onClick: () => setShowCreate(false), children: "\u53D6\u6D88" }), _jsxs(Button, { type: "submit", "aria-label": "\u521B\u5EFA API Key", disabled: creating || !name.trim(), children: [creating ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, creating ? '正在创建…' : '创建 API Key'] })] })] })] }) }), !clientConfigurationBridge ? _jsx("p", { className: "text-xs text-muted-foreground", children: "\u5F53\u524D Web \u73AF\u5883\u53EF\u590D\u5236\u914D\u7F6E\uFF1B\u81EA\u52A8\u5E94\u7528\u548C\u64A4\u56DE\u9700\u8981\u672C\u673A\u8FDE\u63A5\u3002" }) : null, _jsx("div", { className: "space-y-2", children: loading ? (_jsxs("div", { className: "flex items-center gap-2 py-6 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "\u6B63\u5728\u8BFB\u53D6 API Keys"] })) : keys.length === 0 ? (_jsx("p", { className: "py-8 text-center text-sm text-muted-foreground", children: "\u5C1A\u672A\u521B\u5EFA API Key\u3002" })) : (_jsx("div", { className: "rounded-xl border border-border/70", children: keys.map((record) => {
251
+ const disabled = Boolean(record.disabledAt);
252
+ const label = record.name || '未命名 API Key';
253
+ const busy = Boolean(busyKeyId);
254
+ const appliedClients = AI_CONNECTIONS_CLIENTS.filter((clientId) => applications[clientId] === record.id);
255
+ const hasApplication = appliedClients.length > 0;
256
+ return (_jsxs("div", { "data-key-state": disabled ? 'disabled' : 'active', className: cn('flex min-w-0 items-center gap-1 border-b border-border/60 px-2 py-2.5 sm:gap-2 sm:px-4 first:rounded-t-xl last:rounded-b-xl last:border-b-0', disabled ? 'bg-muted/30 text-muted-foreground' : 'bg-emerald-500/[0.035]'), children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 sm:gap-2", children: [_jsx("span", { className: "min-w-0 truncate text-sm font-medium", title: label, children: label }), _jsx("code", { dir: "rtl", className: "min-w-0 max-w-36 truncate text-left text-xs text-muted-foreground", children: _jsx("span", { dir: "ltr", children: record.maskedHint ?? suffixHint(record.id) }) }), _jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8 shrink-0", "aria-label": `复制 ${label}`, title: copied[record.id] === 'key' ? '已复制' : '复制 API Key', disabled: busy, onClick: () => void copyExistingConfiguration(record, 'key'), children: copied[record.id] === 'key' ? _jsx(Check, { className: "h-4 w-4 text-emerald-600" }) : _jsx(Copy, { className: "h-4 w-4" }) }), _jsx("span", { role: "status", "aria-live": "polite", className: "sr-only", children: copied[record.id] === 'key' ? 'API Key 已复制' : '' }), hasApplication ? _jsx("div", { className: "flex shrink-0 items-center gap-1 text-muted-foreground", children: appliedClients.map((clientId) => _jsx("span", { role: "img", "aria-label": `${AI_CLIENT_LABELS[clientId]} 已应用`, title: `${AI_CLIENT_LABELS[clientId]} 已应用`, className: "shrink-0", children: _jsx(AiClientIcon, { client: clientId }) }, clientId)) }) : null] }), _jsx("span", { className: "hidden shrink-0 text-xs text-muted-foreground [@container(min-width:850px)]:inline", children: record.lastUsedAt ? `最后使用 ${formatTimestamp(record.lastUsedAt)}` : '暂无调用记录 · 尚未使用' }), _jsxs("details", { "data-client-menu": record.id, open: openMenuKeyId === record.id, className: "relative shrink-0", onKeyDown: (event) => {
257
+ if (event.key !== 'Escape')
258
+ return;
259
+ event.preventDefault();
260
+ setOpenMenuKeyId(undefined);
261
+ event.currentTarget.querySelector('summary')?.focus();
262
+ }, children: [_jsxs("summary", { role: "button", "aria-label": `${label} 客户端配置`, "aria-expanded": openMenuKeyId === record.id, onClick: (event) => {
263
+ event.preventDefault();
264
+ if (busy && openMenuKeyId !== record.id)
265
+ return;
266
+ setOpenMenuKeyId((current) => current === record.id ? undefined : record.id);
267
+ }, className: "inline-flex min-h-9 max-w-full cursor-pointer list-none items-center gap-2 rounded-md border border-input bg-background px-2 text-sm outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring [&::-webkit-details-marker]:hidden", children: [_jsx("span", { children: "\u5E94\u7528" }), _jsx(ChevronDown, { "aria-hidden": "true", className: "h-3.5 w-3.5 shrink-0" })] }), _jsx("div", { role: "group", "aria-label": `${label} 客户端选项`, className: "absolute right-0 top-full z-20 mt-1 w-72 max-w-[calc(100cqw-6rem)] space-y-1 rounded-lg border border-border bg-popover p-2 text-popover-foreground shadow-lg", children: AI_CONNECTIONS_CLIENTS.map((clientId) => {
268
+ const application = applications[clientId];
269
+ const checked = application === record.id;
270
+ const copiedClient = copied[record.id] === clientId;
271
+ return _jsxs("div", { className: "flex items-center gap-2 rounded-md px-2 py-1.5", children: [clientConfigurationBridge ? _jsxs("label", { className: "flex min-w-0 flex-1 cursor-pointer items-center gap-2 text-sm", children: [_jsx("input", { type: "checkbox", "aria-label": `${label} 应用到 ${AI_CLIENT_LABELS[clientId]}`, checked: checked, disabled: busy || (disabled && !checked), className: "h-4 w-4 shrink-0 accent-primary", onChange: (event) => {
272
+ if (event.target.checked)
273
+ void applyExistingConfiguration(record, clientId);
274
+ else
275
+ void restore(record, clientId);
276
+ } }), _jsx("span", { children: AI_CLIENT_LABELS[clientId] }), applying?.keyId === record.id && applying.clientId === clientId
277
+ ? _jsx("span", { role: "status", className: "text-xs text-muted-foreground", children: "\u5E94\u7528\u4E2D\u2026" })
278
+ : null] }) : _jsx("span", { className: "min-w-0 flex-1 text-sm", children: AI_CLIENT_LABELS[clientId] }), clientConfigurationBridge ? _jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8 shrink-0", "aria-label": `刷新 ${label} 的 ${AI_CLIENT_LABELS[clientId]} endpoint`, title: checked ? '保留当前 Key,刷新客户端连接地址' : '应用后可刷新连接地址', disabled: busy || disabled || !checked, onClick: () => void applyExistingConfiguration(record, clientId, true), children: _jsx(RefreshCw, { className: cn('h-4 w-4', applying?.keyId === record.id && applying.clientId === clientId && 'animate-spin') }) }) : null, _jsx("span", { role: "status", "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: copiedClient ? '已复制' : '' }), _jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8 shrink-0", "aria-label": `复制 ${label} 的 ${AI_CLIENT_LABELS[clientId]} 配置`, title: copiedClient ? '已复制' : `复制 ${AI_CLIENT_LABELS[clientId]} 配置`, disabled: busy, onClick: () => void copyExistingConfiguration(record, clientId), children: copiedClient ? _jsx(Check, { className: "h-4 w-4 text-emerald-600" }) : _jsx(Copy, { className: "h-4 w-4" }) })] }, clientId);
279
+ }) })] }), _jsxs("div", { className: "flex shrink-0 items-center", children: [record.kind !== 'client-credentials' ? (_jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", "aria-pressed": !disabled, "aria-label": `${disabled ? '启用' : '停用'} ${label}`, title: `${disabled ? '启用' : '停用'} ${label}`, disabled: busy, onClick: () => void setEnabled(record, disabled), children: _jsx(Power, { className: cn('h-4 w-4', !disabled && 'text-emerald-600') }) })) : null, _jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", "aria-label": `删除 ${label}`, title: hasApplication ? '请先撤回已应用的配置' : `删除 ${label}`, disabled: busy || hasApplication, onClick: () => void remove(record), children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] }, record.id));
280
+ }) })) })] }));
155
281
  }
156
282
  function suffixHint(id) {
157
283
  return `••••••••${id.slice(-8)}`;
@@ -161,8 +287,18 @@ function formatTimestamp(value) {
161
287
  return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
162
288
  }
163
289
  function errorMessage(error) {
164
- return error instanceof Error && error.message ? error.message : 'API Key 操作失败,请重试。';
165
- }
166
- function isDefined(value) {
167
- return value !== undefined;
290
+ const message = error instanceof Error ? error.message.trim() : '';
291
+ if (/failed to fetch|networkerror|load failed/i.test(message)) {
292
+ return '无法连接配置服务,请检查连接后重试。';
293
+ }
294
+ if (/EACCES|permission denied/i.test(message)) {
295
+ return '没有权限写入配置文件,请检查文件权限后重试。';
296
+ }
297
+ if (message === 'Client configuration could not be verified locally.') {
298
+ return '配置文件写入后的本地检查未通过,请检查文件内容和权限后重试。';
299
+ }
300
+ if (!message || /^(?:AI Connection |AI client configuration )?request failed(?:\. Please try again\.)?$/i.test(message)) {
301
+ return 'API Key 操作失败,请重试。';
302
+ }
303
+ return message;
168
304
  }
@@ -1,9 +1,9 @@
1
- import type { AiConnectAttempt, AiConnectionsMode, AiGatewayModel, AiProviderCredentialSummary, AiProviderOffering, AiProviderSummary } from './ai-connections-client';
1
+ import type { AiConnectAttempt, AiConnectionsMode, AiGatewayModel, AiProviderCredentialSummary, AiProviderAuthorizationMethod, AiProviderOffering, AiProviderSummary } from './ai-connections-client';
2
2
  import { type AiOfferingActionError, type AiOfferingQuotaState } from './AiCredentialPoolSection';
3
3
  export type { AiProviderDefinition } from './controller';
4
4
  import type { AiProviderDefinition } from './controller';
5
5
  export type ProviderConnectionState = 'unknown' | 'pending' | 'configured' | 'connected' | 'disconnected' | 'reauthRequired' | 'failed';
6
- export declare function AiProviderCard({ definition, product, status, accountLabel, attempt, attemptOfferingId, apiKey, baseUrl, busy, disabled, error, quotas, models, verifyPending, onApiKeyChange, onBaseUrlChange, onBeginApiKey, onBeginOffering, onBeginBrowser, onSaveApiKey, onDisconnect, onCreateApiKeyCredential, onCreateLocalCredential, onUpdateCredential, onDeleteCredential, onTestCredential, onReorderCredentials, onRefreshQuota, onVerify, onAddModel, onEditModel, onDeleteModel, selectedModelIds, onModelSelectionChange, onDismissError, }: {
6
+ export declare function AiProviderCard({ definition, product, status, accountLabel, attempt, attemptOfferingId, apiKey, baseUrl, busy, disabled, error, quotas, models, verifyPending, onApiKeyChange, onBaseUrlChange, onBeginApiKey, onBeginOffering, onCancelConnect, onBeginBrowser, onSaveApiKey, onDisconnect, onCreateApiKeyCredential, onCreateLocalCredential, onUpdateCredential, onDeleteCredential, onTestCredential, onReorderCredentials, onRefreshQuota, onVerify, onAddModel, onEditModel, onDeleteModel, selectedModelIds, modelSelectionStatus, onModelSelectionChange, onDismissError, }: {
7
7
  definition: AiProviderDefinition;
8
8
  product?: AiProviderSummary;
9
9
  status: ProviderConnectionState;
@@ -21,7 +21,8 @@ export declare function AiProviderCard({ definition, product, status, accountLab
21
21
  onApiKeyChange: (value: string) => void;
22
22
  onBaseUrlChange?: (value: string) => void;
23
23
  onBeginApiKey: () => void;
24
- onBeginOffering?: (offering: AiProviderOffering, mode: AiConnectionsMode) => void;
24
+ onBeginOffering?: (offering: AiProviderOffering, mode: AiConnectionsMode, method?: AiProviderAuthorizationMethod) => void;
25
+ onCancelConnect?: (attempt: Pick<AiConnectAttempt, 'attemptId' | 'state' | 'signature'>) => void;
25
26
  onBeginBrowser: () => void;
26
27
  onSaveApiKey: () => void;
27
28
  onDisconnect: (credential?: AiProviderCredentialSummary) => void;
@@ -32,7 +33,7 @@ export declare function AiProviderCard({ definition, product, status, accountLab
32
33
  proxyUrl?: string;
33
34
  priority: number;
34
35
  }) => Promise<void>;
35
- onCreateLocalCredential?: (offering: AiProviderOffering) => Promise<void>;
36
+ onCreateLocalCredential?: (offering: AiProviderOffering, method?: AiProviderAuthorizationMethod) => Promise<void>;
36
37
  onUpdateCredential?: (credential: AiProviderCredentialSummary, patch: {
37
38
  label?: string;
38
39
  enabled?: boolean;
@@ -48,6 +49,7 @@ export declare function AiProviderCard({ definition, product, status, accountLab
48
49
  onAddModel?: () => void;
49
50
  onEditModel?: (model: AiGatewayModel) => void;
50
51
  onDeleteModel?: (model: AiGatewayModel) => void;
52
+ modelSelectionStatus?: 'saving' | 'saved' | 'error';
51
53
  selectedModelIds?: string[];
52
54
  onModelSelectionChange?: (provider: AiProviderSummary['id'], modelIds: string[]) => void;
53
55
  onDismissError?: () => void;