@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
@@ -52,6 +52,7 @@ __export(exports_client_config, {
52
52
  normalizeClientProfile: () => normalizeClientProfile,
53
53
  looksLikePreviousXpodValue: () => looksLikePreviousXpodValue,
54
54
  hashWebId: () => hashWebId,
55
+ contentFingerprint: () => contentFingerprint,
55
56
  activeModelReferences: () => activeModelReferences,
56
57
  PiConfigAdapter: () => PiConfigAdapter,
57
58
  CodexConfigAdapter: () => CodexConfigAdapter,
@@ -90,7 +91,11 @@ class AiClientConfigTransaction {
90
91
  }
91
92
  }
92
93
  const snapshots = await Promise.all(writes.map((write) => this.snapshot(write.path)));
94
+ for (const write of writes) {
95
+ this.assertExpectedContent(write, snapshots.find((snapshot) => snapshot.path === write.path).content);
96
+ }
93
97
  const staged = new Map;
98
+ const applied = [];
94
99
  try {
95
100
  for (const write of writes) {
96
101
  const snapshot = snapshots.find((candidate) => candidate.path === write.path);
@@ -105,16 +110,21 @@ class AiClientConfigTransaction {
105
110
  }
106
111
  }
107
112
  for (const write of writes) {
113
+ if (write.expectedContentHash !== undefined) {
114
+ this.assertExpectedContent(write, (await this.snapshot(write.path)).content);
115
+ }
108
116
  if (write.content === null) {
109
117
  await fs.promises.rm(write.path, { force: true });
118
+ applied.push(write);
110
119
  } else {
111
120
  await this.rename(staged.get(write.path), write.path);
121
+ applied.push(write);
112
122
  await fs.promises.chmod(write.path, 384);
113
123
  await this.syncDirectory(path.dirname(write.path));
114
124
  }
115
125
  }
116
126
  } catch (error) {
117
- await this.rollback(snapshots);
127
+ await this.rollback(snapshots, applied);
118
128
  throw error;
119
129
  } finally {
120
130
  await Promise.all([...staged.values()].map((tempPath) => fs.promises.rm(tempPath, { force: true }).catch(() => {
@@ -178,20 +188,43 @@ class AiClientConfigTransaction {
178
188
  await fs.promises.chmod(filePath, 384);
179
189
  await this.syncDirectory(path.dirname(filePath));
180
190
  }
181
- async rollback(snapshots) {
182
- for (const snapshot of [...snapshots].reverse()) {
191
+ assertExpectedContent(write, content) {
192
+ if (write.expectedContentHash === undefined)
193
+ return;
194
+ const hash = content === undefined ? undefined : crypto.createHash("sha256").update(content).digest("hex");
195
+ if (hash !== write.expectedContentHash) {
196
+ throw new Error(`AI client configuration changed since planning: ${write.path}; retry with a fresh plan`);
197
+ }
198
+ }
199
+ async rollback(snapshots, applied) {
200
+ for (const write of [...applied].reverse()) {
201
+ const snapshot = snapshots.find((candidate) => candidate.path === write.path);
202
+ let tempPath;
183
203
  try {
204
+ if (!await this.stillContainsWrite(write))
205
+ continue;
184
206
  if (!snapshot.existed) {
185
207
  await fs.promises.rm(snapshot.path, { force: true });
186
208
  continue;
187
209
  }
188
- const tempPath = await this.stage(snapshot.path, snapshot.content.toString("utf8"));
210
+ tempPath = await this.stage(snapshot.path, snapshot.content.toString("utf8"));
211
+ if (!await this.stillContainsWrite(write))
212
+ continue;
189
213
  await this.rename(tempPath, snapshot.path);
190
214
  await fs.promises.chmod(snapshot.path, snapshot.mode ?? 384);
191
215
  await this.syncDirectory(path.dirname(snapshot.path));
192
- } catch {}
216
+ } catch {} finally {
217
+ if (tempPath)
218
+ await fs.promises.rm(tempPath, { force: true }).catch(() => {
219
+ return;
220
+ });
221
+ }
193
222
  }
194
223
  }
224
+ async stillContainsWrite(write) {
225
+ const current = await this.snapshot(write.path);
226
+ return write.content === null ? !current.existed : current.content?.equals(Buffer.from(write.content, "utf8")) === true;
227
+ }
195
228
  async syncDirectory(directory) {
196
229
  let handle;
197
230
  try {
@@ -227,6 +260,10 @@ class AiClientConfigError extends Error {
227
260
  function hashWebId(webId) {
228
261
  return crypto2.createHash("sha256").update(webId.trim(), "utf8").digest("hex");
229
262
  }
263
+ function contentHash(value) {
264
+ return crypto2.createHash("sha256").update(value, "utf8").digest("hex");
265
+ }
266
+ var contentFingerprint = contentHash;
230
267
  function normalizeV1Endpoint(endpoint) {
231
268
  const normalized = endpoint.trim().replace(/\/+$/, "");
232
269
  return normalized.endsWith("/v1") ? normalized : `${normalized}/v1`;
@@ -314,7 +351,7 @@ function normalizeActiveModels(models) {
314
351
  function normalizeClientProfile(profile) {
315
352
  return {
316
353
  ...profile,
317
- model: resolveActiveModel(profile)
354
+ model: profile.activeModels?.length ? resolveActiveModel(profile) : profile.model?.trim() || undefined
318
355
  };
319
356
  }
320
357
  function stripLegacyXpodObject(value) {
@@ -346,9 +383,22 @@ class BaseAiClientConfigAdapter {
346
383
  }
347
384
  async inspect() {
348
385
  const state = await this.readState();
386
+ let projectionMatches;
387
+ if (state?.projectionHashes) {
388
+ projectionMatches = true;
389
+ for (const filePath of this.configPaths) {
390
+ await this.rejectSymlink(filePath);
391
+ const content = await this.readOptional(filePath);
392
+ if (content === undefined || contentHash(content) !== state.projectionHashes[filePath]) {
393
+ projectionMatches = false;
394
+ }
395
+ }
396
+ }
349
397
  return {
350
398
  ownership: state ? "owned" : "unowned",
351
399
  ...state ? { webIdHash: state.webIdHash } : {},
400
+ ...projectionMatches === undefined ? {} : { projectionMatches },
401
+ ...projectionMatches && /^[a-f0-9]{64}$/u.test(state?.apiKeyFingerprint ?? "") ? { apiKeyFingerprint: state.apiKeyFingerprint } : {},
352
402
  configPaths: [...this.configPaths]
353
403
  };
354
404
  }
@@ -365,8 +415,14 @@ class BaseAiClientConfigAdapter {
365
415
  contents.set(filePath, await this.readOptional(filePath));
366
416
  }
367
417
  const projected = await this.project(normalizedProfile, contents);
418
+ for (const filePath of projected.keys()) {
419
+ if (!contents.has(filePath)) {
420
+ await this.rejectSymlink(filePath);
421
+ contents.set(filePath, await this.readOptional(filePath));
422
+ }
423
+ }
368
424
  const timestamp = Date.now();
369
- const files = this.configPaths.map((filePath) => {
425
+ const files = [...projected.keys()].map((filePath) => {
370
426
  const prior = currentState?.files.find((file) => file.path === filePath);
371
427
  const existed = contents.get(filePath) !== undefined;
372
428
  return prior ?? {
@@ -379,12 +435,14 @@ class BaseAiClientConfigAdapter {
379
435
  path: filePath,
380
436
  content,
381
437
  backupPath: files.find((file) => file.path === filePath)?.backupPath,
382
- createBackup: !currentState && contents.get(filePath) !== undefined
438
+ createBackup: !currentState?.files.some((file) => file.path === filePath) && contents.get(filePath) !== undefined
383
439
  }));
384
440
  const state = {
385
441
  version: 1,
386
442
  client: this.client,
387
443
  webIdHash: ownerHash,
444
+ apiKeyFingerprint: contentHash(profileApiKey(normalizedProfile)),
445
+ projectionHashes: Object.fromEntries([...projected].map(([filePath, content]) => [filePath, contentHash(content)])),
388
446
  files
389
447
  };
390
448
  writes.push({ path: this.statePath, content: stringifyJson(state) });
@@ -411,10 +469,17 @@ class BaseAiClientConfigAdapter {
411
469
  if (state.webIdHash !== hashWebId(webId)) {
412
470
  throw new Error(`${this.client} AI Connection projection is owned by another WebID`);
413
471
  }
414
- const writes = [];
472
+ const current = new Map;
415
473
  for (const file of state.files) {
416
474
  await this.rejectSymlink(file.path);
417
- const current = await this.readOptional(file.path);
475
+ current.set(file.path, await this.readOptional(file.path));
476
+ }
477
+ const currentFingerprint = await this.currentApiKeyFingerprint(current);
478
+ if (state.apiKeyFingerprint && currentFingerprint && currentFingerprint !== state.apiKeyFingerprint) {
479
+ throw new Error(`${this.client} API key changed since projection was applied; refusing restore to avoid data loss`);
480
+ }
481
+ const writes = [];
482
+ for (const file of state.files) {
418
483
  let original;
419
484
  if (file.existed) {
420
485
  if (!file.backupPath) {
@@ -428,7 +493,7 @@ class BaseAiClientConfigAdapter {
428
493
  }
429
494
  writes.push({
430
495
  path: file.path,
431
- content: await this.restoreFile(file.path, current, original, file.existed)
496
+ content: await this.restoreFile(file.path, current.get(file.path), original, file.existed)
432
497
  });
433
498
  }
434
499
  writes.push({ path: this.statePath, content: null });
@@ -437,6 +502,9 @@ class BaseAiClientConfigAdapter {
437
502
  normalizeProfile(profile) {
438
503
  return normalizeClientProfile(profile);
439
504
  }
505
+ async currentApiKeyFingerprint(_current) {
506
+ return;
507
+ }
440
508
  async readOptional(filePath) {
441
509
  try {
442
510
  return await fs2.promises.readFile(filePath, "utf8");
@@ -506,67 +574,154 @@ var END = "# <<< xpod-ai-connections managed";
506
574
  class CodexConfigAdapter extends BaseAiClientConfigAdapter {
507
575
  configPath;
508
576
  authPath;
577
+ catalogPath;
578
+ nativeModelsPath;
509
579
  constructor(options = {}) {
510
580
  const codexHome = path3.join(options.homeDir ?? os.homedir(), ".codex");
511
581
  const configPath = path3.join(codexHome, "config.toml");
512
582
  const authPath = path3.join(codexHome, "auth.json");
513
- super("codex", "codex", [configPath, authPath], codexHome);
583
+ super("codex", "codex", [configPath], codexHome);
514
584
  this.configPath = configPath;
515
585
  this.authPath = authPath;
586
+ this.catalogPath = path3.join(codexHome, "xpod-model-catalog.json");
587
+ this.nativeModelsPath = path3.join(codexHome, "models_cache.json");
516
588
  }
517
589
  normalizeProfile(profile) {
518
- if (!profile.model?.trim() && (!profile.activeModels || profile.activeModels.length === 0)) {
519
- return { ...profile, model: undefined };
590
+ return profile.model?.trim() ? super.normalizeProfile(profile) : { ...profile, model: undefined };
591
+ }
592
+ async inspect() {
593
+ const inspection = await super.inspect();
594
+ const state = await this.readState();
595
+ if (!state?.files.some((file) => file.path === this.catalogPath))
596
+ return inspection;
597
+ await this.rejectSymlink(this.catalogPath);
598
+ const catalog = await this.readOptional(this.catalogPath);
599
+ if (catalog !== undefined && contentFingerprint(catalog) === state.projectionHashes?.[this.catalogPath]) {
600
+ return { ...inspection, configPaths: [...inspection.configPaths, this.catalogPath] };
520
601
  }
521
- return super.normalizeProfile(profile);
602
+ const withoutFingerprint = { ...inspection };
603
+ delete withoutFingerprint.apiKeyFingerprint;
604
+ return { ...withoutFingerprint, projectionMatches: false, configPaths: [...inspection.configPaths, this.catalogPath] };
605
+ }
606
+ async plan(profile) {
607
+ const plan = await super.plan(profile);
608
+ const state = await this.readState();
609
+ const legacyAuth = state?.files.find((file) => file.path === this.authPath);
610
+ if (!legacyAuth)
611
+ return plan;
612
+ await this.rejectSymlink(this.authPath);
613
+ const current = await this.readOptional(this.authPath);
614
+ if (!this.ownsLegacyAuth(current, state?.apiKeyFingerprint))
615
+ return plan;
616
+ let original;
617
+ if (legacyAuth.existed) {
618
+ if (!legacyAuth.backupPath)
619
+ throw new Error("Missing Codex auth.json backup metadata");
620
+ await this.rejectSymlink(legacyAuth.backupPath);
621
+ original = await this.readOptional(legacyAuth.backupPath);
622
+ if (original === undefined)
623
+ throw new Error("Missing Codex auth.json backup; refusing to guess login state");
624
+ }
625
+ plan.writes.splice(plan.writes.length - 1, 0, {
626
+ path: this.authPath,
627
+ content: restoreLegacyAuth(current, original, legacyAuth.existed),
628
+ expectedContentHash: contentFingerprint(current)
629
+ });
630
+ return plan;
631
+ }
632
+ ownsLegacyAuth(current, fingerprint) {
633
+ if (!fingerprint || current === undefined)
634
+ return false;
635
+ const auth = parseJsonObject(current, "Codex auth.json");
636
+ return typeof auth.OPENAI_API_KEY === "string" && contentFingerprint(auth.OPENAI_API_KEY) === fingerprint;
522
637
  }
523
638
  async project(profile, current) {
524
- const config = stripRootLevelModelKeys(this.removeManagedBlock(current.get(this.configPath) ?? "")).trimEnd();
639
+ const baseContent = current.get(this.configPath) ?? "";
640
+ let nativeModels = [];
641
+ if (profile.activeModels?.length) {
642
+ const nativeCatalog = await this.readOptional(this.nativeModelsPath);
643
+ if (nativeCatalog !== undefined)
644
+ nativeModels = readCatalogModels(nativeCatalog, "Codex native model cache");
645
+ }
646
+ let catalog = codexModelCatalog(profile.activeModels, nativeModels);
647
+ const state = await this.readState();
648
+ const ownsCatalog = state?.files.some((file) => file.path === this.catalogPath);
649
+ if (catalog !== undefined || ownsCatalog) {
650
+ await this.rejectSymlink(this.catalogPath);
651
+ const existingCatalog = await this.readOptional(this.catalogPath);
652
+ current.set(this.catalogPath, existingCatalog);
653
+ catalog ??= existingCatalog;
654
+ if (catalog === undefined)
655
+ throw new Error("Codex model catalog is missing; refresh with the current Gateway models");
656
+ }
657
+ const unmanagedConfig = this.removeManagedBlock(baseContent);
658
+ if (hasUnmanagedModelProviderTable(unmanagedConfig)) {
659
+ throw new Error("Codex config.toml already defines [model_providers.xpod] outside managed block");
660
+ }
661
+ const config = stripRootLevelModelKeys(unmanagedConfig).trim();
525
662
  const model = profile.model?.trim();
526
- const block = [
663
+ const rootBlock = [
527
664
  START,
528
665
  'model_provider = "xpod"',
529
- ...model ? [`model = ${JSON.stringify(model)}`] : [],
666
+ ...model ? [`model = ${JSON.stringify(model)}`] : rootLevelLines(baseContent, "model"),
667
+ ...catalog !== undefined ? [`model_catalog_json = ${JSON.stringify(this.catalogPath)}`] : rootLevelLines(baseContent, "model_catalog_json"),
668
+ END
669
+ ].join(`
670
+ `);
671
+ const providerBlock = [
672
+ START,
530
673
  "",
531
674
  "[model_providers.xpod]",
532
675
  'name = "Xpod AI Connection"',
533
676
  `base_url = ${JSON.stringify(normalizeV1Endpoint(profile.endpoint))}`,
534
677
  'wire_api = "responses"',
535
- "requires_openai_auth = true",
678
+ "requires_openai_auth = false",
679
+ `experimental_bearer_token = ${JSON.stringify(profileApiKey(profile))}`,
536
680
  END,
537
681
  ""
538
682
  ].join(`
539
683
  `);
540
- const auth = parseJsonObject(current.get(this.authPath), "Codex auth.json");
541
- auth.OPENAI_API_KEY = profileApiKey(profile);
542
- return new Map([
543
- [this.configPath, `${config}${config ? `
684
+ const projected = new Map([[this.configPath, `${rootBlock}
544
685
 
545
- ` : ""}${block}`],
546
- [this.authPath, stringifyJson(auth)]
547
- ]);
686
+ ${config}${config ? `
687
+
688
+ ` : ""}${providerBlock}`]]);
689
+ if (catalog !== undefined)
690
+ projected.set(this.catalogPath, catalog);
691
+ return projected;
548
692
  }
549
693
  async verifyProjection(profile) {
550
694
  try {
551
695
  const config = await fs3.promises.readFile(this.configPath, "utf8");
552
- const auth = parseJsonObject(await fs3.promises.readFile(this.authPath, "utf8"), "Codex auth.json");
553
- const modelMatches = profile.model ? config.includes(`model = ${JSON.stringify(profile.model)}`) : !hasRootLevelKey(config, "model");
554
- const ok = config.includes('model_provider = "xpod"') && modelMatches && config.includes(`base_url = ${JSON.stringify(normalizeV1Endpoint(profile.endpoint))}`) && auth.OPENAI_API_KEY === profileApiKey(profile);
696
+ const modelMatches = profile.model ? rootLevelLines(config, "model").some((line) => line.trim() === `model = ${JSON.stringify(profile.model)}`) : true;
697
+ const provider = tableLines(config, "model_providers.xpod");
698
+ const state = await this.readState();
699
+ const ownsCatalog = state?.files.some((file) => file.path === this.catalogPath);
700
+ let catalogMatches = true;
701
+ if (profile.activeModels !== undefined || ownsCatalog) {
702
+ const catalog = await fs3.promises.readFile(this.catalogPath, "utf8");
703
+ const expectedCatalog = codexModelCatalog(profile.activeModels, readCatalogModels(catalog, "Codex model catalog"));
704
+ catalogMatches = rootLevelLines(config, "model_catalog_json").includes(`model_catalog_json = ${JSON.stringify(this.catalogPath)}`) && contentFingerprint(catalog) === state?.projectionHashes?.[this.catalogPath] && (expectedCatalog === undefined || catalog === expectedCatalog);
705
+ }
706
+ const ok = rootLevelLines(config, "model_provider").some((line) => line.trim() === 'model_provider = "xpod"') && modelMatches && catalogMatches && provider.includes(`base_url = ${JSON.stringify(normalizeV1Endpoint(profile.endpoint))}`) && provider.includes('wire_api = "responses"') && provider.includes("requires_openai_auth = false") && provider.includes(`experimental_bearer_token = ${JSON.stringify(profileApiKey(profile))}`);
555
707
  return ok ? { ok: true } : { ok: false, reason: "Codex projection differs from the requested connection" };
556
708
  } catch (error) {
557
709
  return { ok: false, reason: String(error) };
558
710
  }
559
711
  }
560
712
  async restoreFile(filePath, current, original, originallyExisted) {
561
- if (filePath === this.authPath) {
562
- const restored2 = parseJsonObject(current, "Codex auth.json");
563
- const before = parseJsonObject(original, "Codex original auth.json");
564
- if (Object.prototype.hasOwnProperty.call(before, "OPENAI_API_KEY") && !looksLikePreviousXpodValue(before.OPENAI_API_KEY)) {
565
- restored2.OPENAI_API_KEY = before.OPENAI_API_KEY;
566
- } else {
567
- delete restored2.OPENAI_API_KEY;
713
+ if (filePath === this.catalogPath) {
714
+ const state = await this.readState();
715
+ if (current === undefined || contentFingerprint(current) !== state?.projectionHashes?.[this.catalogPath]) {
716
+ throw new Error("Codex model catalog changed since projection was applied; refusing restore to avoid data loss");
568
717
  }
569
- return !originallyExisted && Object.keys(restored2).length === 0 ? null : stringifyJson(restored2);
718
+ return originallyExisted ? original : null;
719
+ }
720
+ if (filePath === this.authPath) {
721
+ const state = await this.readState();
722
+ if (!this.ownsLegacyAuth(current, state?.apiKeyFingerprint))
723
+ return current ?? null;
724
+ return restoreLegacyAuth(current, original, originallyExisted);
570
725
  }
571
726
  let restored = this.removeManagedBlock(current ?? "").trim();
572
727
  const hasCurrentProvider = hasRootLevelKey(restored, "model_provider");
@@ -578,19 +733,51 @@ class CodexConfigAdapter extends BaseAiClientConfigAdapter {
578
733
  restored = insertRootLevelLines(restored, originalRoot);
579
734
  }
580
735
  }
736
+ if (!hasRootLevelKey(restored, "model_catalog_json") && !originalHasXpodProjection) {
737
+ const originalCatalog = rootLevelLines(originalContent, "model_catalog_json");
738
+ if (originalCatalog.length > 0)
739
+ restored = insertRootLevelLines(restored, originalCatalog);
740
+ }
581
741
  return !originallyExisted && !restored ? null : `${restored}${restored ? `
582
742
  ` : ""}`;
583
743
  }
744
+ async currentApiKeyFingerprint(current) {
745
+ const token = tableLines(current.get(this.configPath) ?? "", "model_providers.xpod").find((line) => /^experimental_bearer_token\s*=/u.test(line));
746
+ if (token) {
747
+ const value = JSON.parse(token.slice(token.indexOf("=") + 1).trim());
748
+ return typeof value === "string" ? contentFingerprint(value) : undefined;
749
+ }
750
+ if (current.has(this.authPath)) {
751
+ const auth = parseJsonObject(current.get(this.authPath), "Codex auth.json");
752
+ return typeof auth.OPENAI_API_KEY === "string" ? contentFingerprint(auth.OPENAI_API_KEY) : undefined;
753
+ }
754
+ return;
755
+ }
584
756
  removeManagedBlock(content) {
585
- const start = content.indexOf(START);
586
- if (start < 0)
587
- return content;
588
- const end = content.indexOf(END, start);
589
- if (end < 0)
590
- throw new Error("Codex xpod managed block is incomplete");
591
- return `${content.slice(0, start)}${content.slice(end + END.length)}`;
757
+ let start = content.indexOf(START);
758
+ while (start >= 0) {
759
+ const end = content.indexOf(END, start);
760
+ if (end < 0)
761
+ throw new Error("Codex xpod managed block is incomplete");
762
+ content = `${content.slice(0, start)}${content.slice(end + END.length)}`;
763
+ start = content.indexOf(START);
764
+ }
765
+ return content;
592
766
  }
593
767
  }
768
+ function tableLines(content, table) {
769
+ const lines = [];
770
+ let selected = false;
771
+ for (const line of content.split(`
772
+ `)) {
773
+ if (isTomlTableHeader(line)) {
774
+ selected = line.trim() === `[${table}]`;
775
+ } else if (selected) {
776
+ lines.push(line.trim());
777
+ }
778
+ }
779
+ return lines;
780
+ }
594
781
  function stripRootLevelModelKeys(content) {
595
782
  const lines = content.split(`
596
783
  `);
@@ -643,13 +830,151 @@ ${normalizedContent}` : ""}`;
643
830
  function isTomlTableHeader(line) {
644
831
  return /^\s*\[\[?[^\]]+\]\]?\s*(?:#.*)?$/u.test(line);
645
832
  }
833
+ function hasUnmanagedModelProviderTable(content) {
834
+ for (const line of content.split(`
835
+ `)) {
836
+ if (!isTomlTableHeader(line) || /^\s*\[\[/.test(line)) {
837
+ continue;
838
+ }
839
+ if (normalizeTomlTableName(line) === "model_providers.xpod") {
840
+ return true;
841
+ }
842
+ }
843
+ return false;
844
+ }
845
+ function normalizeTomlTableName(line) {
846
+ const match = /^\s*\[\s*([^\]]+)\s*\]\s*(?:#.*)?$/u.exec(line);
847
+ if (!match)
848
+ return;
849
+ const rawName = match[1].trim();
850
+ const segments = parseTomlTableSegments(rawName);
851
+ return segments.length === 1 ? undefined : normalizeTomlDottedKey(rawName);
852
+ }
853
+ function normalizeTomlDottedKey(content) {
854
+ const parts = parseTomlTableSegments(content);
855
+ return parts.map((part) => removeTomlQuotes(part)).join(".");
856
+ }
857
+ function parseTomlTableSegments(input) {
858
+ const parts = [];
859
+ let current = "";
860
+ let quote = null;
861
+ for (let i = 0;i < input.length; i++) {
862
+ const char = input[i];
863
+ if (quote) {
864
+ if (char === quote) {
865
+ quote = null;
866
+ continue;
867
+ }
868
+ current += char;
869
+ continue;
870
+ }
871
+ if (char === '"' || char === "'") {
872
+ quote = char;
873
+ continue;
874
+ }
875
+ if (char === ".") {
876
+ parts.push(current.trim());
877
+ current = "";
878
+ continue;
879
+ }
880
+ current += char;
881
+ }
882
+ parts.push(current.trim());
883
+ return parts.map((part) => part.trim()).filter((part) => part.length > 0);
884
+ }
885
+ function removeTomlQuotes(value) {
886
+ if (value.length === 0)
887
+ return value;
888
+ const first = value[0];
889
+ const last = value[value.length - 1];
890
+ if (first === '"' && last === '"' || first === "'" && last === "'") {
891
+ return value.slice(1, -1);
892
+ }
893
+ return value;
894
+ }
646
895
  function isRootModelKey(line) {
647
896
  return rootModelKey(line) !== undefined;
648
897
  }
649
898
  function rootModelKey(line) {
650
- const match = /^\s*(model_provider|model)\s*=/.exec(line);
899
+ const match = /^\s*(model_provider|model|model_catalog_json)\s*=/.exec(line);
651
900
  return match?.[1];
652
901
  }
902
+ function restoreLegacyAuth(current, original, originallyExisted) {
903
+ const restored = parseJsonObject(current, "Codex auth.json");
904
+ const before = parseJsonObject(original, "Codex original auth.json");
905
+ if (restored.auth_mode === "apikey") {
906
+ if (Object.prototype.hasOwnProperty.call(before, "auth_mode")) {
907
+ restored.auth_mode = before.auth_mode;
908
+ } else {
909
+ delete restored.auth_mode;
910
+ }
911
+ }
912
+ if (Object.prototype.hasOwnProperty.call(before, "OPENAI_API_KEY") && !looksLikePreviousXpodValue(before.OPENAI_API_KEY)) {
913
+ restored.OPENAI_API_KEY = before.OPENAI_API_KEY;
914
+ } else {
915
+ delete restored.OPENAI_API_KEY;
916
+ }
917
+ return !originallyExisted && Object.keys(restored).length === 0 ? null : stringifyJson(restored);
918
+ }
919
+ function codexModelCatalog(models, nativeModels = []) {
920
+ if (models === undefined)
921
+ return;
922
+ const available = models.filter((model) => model.availability === undefined || model.availability === "available");
923
+ if (available.length === 0)
924
+ throw new AiClientConfigError("model_catalog_empty");
925
+ const seen = new Set;
926
+ return stringifyJson({ models: available.map((model, index) => {
927
+ const id = model.id.trim();
928
+ if (!id || seen.has(id))
929
+ throw new Error("Codex model catalog requires unique, non-empty Gateway model ids");
930
+ seen.add(id);
931
+ const native = nativeModels.find((entry) => entry.slug === id);
932
+ const nativeModalities = Array.isArray(native?.input_modalities) ? native.input_modalities : ["text"];
933
+ const declaredModalities = model.inputModalities?.filter((value) => value === "text" || value === "image");
934
+ const modalities = declaredModalities?.filter((value) => !native || nativeModalities.includes(value)) ?? nativeModalities;
935
+ const contextWindow = Number.isSafeInteger(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : undefined;
936
+ const reasoning = codexReasoningLevels(model);
937
+ return {
938
+ slug: id,
939
+ description: "Xpod Gateway model",
940
+ supported_reasoning_levels: reasoning.levels,
941
+ ...reasoning.defaultLevel ? { default_reasoning_level: reasoning.defaultLevel } : {},
942
+ shell_type: "shell_command",
943
+ support_verbosity: false,
944
+ supports_parallel_tool_calls: false,
945
+ truncation_policy: { mode: "tokens", limit: 1e4 },
946
+ experimental_supported_tools: [],
947
+ base_instructions: "You are a coding assistant.",
948
+ ...native,
949
+ display_name: model.displayName?.trim() || native?.display_name || id,
950
+ visibility: "list",
951
+ supported_in_api: true,
952
+ priority: index,
953
+ input_modalities: modalities.length ? [...new Set(modalities)] : ["text"],
954
+ ...contextWindow !== undefined ? { context_window: contextWindow } : {},
955
+ ...contextWindow !== undefined && typeof native?.max_context_window === "number" ? { max_context_window: Math.min(contextWindow, native.max_context_window) } : {}
956
+ };
957
+ }) });
958
+ }
959
+ function codexReasoningLevels(model) {
960
+ if (model.provider !== "deepseek" || !model.capabilities?.includes("reasoning"))
961
+ return { levels: [] };
962
+ return {
963
+ levels: [
964
+ { effort: "low", description: "DeepSeek low reasoning depth" },
965
+ { effort: "high", description: "DeepSeek standard reasoning depth" },
966
+ { effort: "max", description: "DeepSeek maximum reasoning depth" }
967
+ ],
968
+ defaultLevel: "high"
969
+ };
970
+ }
971
+ function readCatalogModels(content, label) {
972
+ const catalog = parseJsonObject(content, label);
973
+ if (!Array.isArray(catalog.models) || catalog.models.some((model) => !model || typeof model !== "object" || Array.isArray(model) || typeof model.slug !== "string")) {
974
+ throw new Error(`${label} must contain a models array with string slugs`);
975
+ }
976
+ return catalog.models;
977
+ }
653
978
  // src/client-config/json-env-adapter.ts
654
979
  var fs4 = __toESM(require("node:fs"));
655
980
  var os2 = __toESM(require("node:os"));
@@ -667,7 +992,8 @@ class JsonEnvAdapter extends BaseAiClientConfigAdapter {
667
992
  const env = settings.env && typeof settings.env === "object" && !Array.isArray(settings.env) ? { ...settings.env } : {};
668
993
  Object.assign(env, this.envProjection(profile));
669
994
  settings.env = env;
670
- settings.model = profile.model;
995
+ if (profile.model)
996
+ settings.model = profile.model;
671
997
  return new Map([[this.settingsPath, stringifyJson(settings)]]);
672
998
  }
673
999
  async verifyProjection(profile) {
@@ -675,7 +1001,7 @@ class JsonEnvAdapter extends BaseAiClientConfigAdapter {
675
1001
  const settings = parseJsonObject(await fs4.promises.readFile(this.settingsPath, "utf8"), `${this.client} settings.json`);
676
1002
  const env = settings.env;
677
1003
  const expected = this.envProjection(profile);
678
- const ok = env !== undefined && settings.model === profile.model && Object.entries(expected).every(([key, value]) => env[key] === value);
1004
+ const ok = env !== undefined && (!profile.model || settings.model === profile.model) && Object.entries(expected).every(([key, value]) => env[key] === value);
679
1005
  return ok ? { ok: true } : {
680
1006
  ok: false,
681
1007
  reason: `${this.client} projection differs from the requested connection`
@@ -760,15 +1086,18 @@ class PiConfigAdapter extends BaseAiClientConfigAdapter {
760
1086
  const settings = parseJsonObject(current.get(this.settingsPath), "Pi settings.json");
761
1087
  const models = parseJsonObject(current.get(this.modelsPath), "Pi models.json");
762
1088
  const providers = models.providers && typeof models.providers === "object" && !Array.isArray(models.providers) ? { ...models.providers } : {};
1089
+ const previous = isObject2(providers.xpod) ? providers.xpod : {};
763
1090
  const model = profile.model;
764
1091
  settings.defaultProvider = "xpod";
765
- settings.defaultModel = model;
1092
+ if (model)
1093
+ settings.defaultModel = model;
766
1094
  providers.xpod = {
1095
+ ...previous,
767
1096
  baseUrl: normalizeV1Endpoint(profile.endpoint),
768
1097
  apiKey: profileApiKey(profile),
769
1098
  authHeader: true,
770
1099
  api: "openai-responses",
771
- models: activeModelReferences(profile)
1100
+ models: profile.activeModels?.length ? activeModelReferences(profile) : model ? mergeModel(previous.models, model) : previous.models ?? []
772
1101
  };
773
1102
  models.providers = providers;
774
1103
  return new Map([
@@ -783,7 +1112,7 @@ class PiConfigAdapter extends BaseAiClientConfigAdapter {
783
1112
  const configuredModels = isObject2(xpod?.models) || Array.isArray(xpod?.models) ? xpod?.models : undefined;
784
1113
  const ok = xpod?.baseUrl === normalizeV1Endpoint(profile.endpoint) && xpod.apiKey === profileApiKey(profile);
785
1114
  const modelIds = Array.isArray(configuredModels) ? configuredModels.map((entry) => isObject2(entry) && typeof entry.id === "string" ? entry.id : undefined).filter((id) => id !== undefined) : [];
786
- return ok && modelIds.includes(profile.model ?? "") ? { ok: true } : { ok: false, reason: "Pi projection differs from the requested connection" };
1115
+ return ok && (!profile.model || modelIds.includes(profile.model)) ? { ok: true } : { ok: false, reason: "Pi projection differs from the requested connection" };
787
1116
  } catch (error) {
788
1117
  return { ok: false, reason: String(error) };
789
1118
  }
@@ -820,3 +1149,7 @@ function restoreOwnedProperty(target, original, key) {
820
1149
  function isObject2(value) {
821
1150
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
822
1151
  }
1152
+ function mergeModel(models, id) {
1153
+ const existing = Array.isArray(models) ? models : [];
1154
+ return existing.some((entry) => isObject2(entry) && entry.id === id) ? existing : [...existing, { id }];
1155
+ }
@@ -15,7 +15,8 @@ class JsonEnvAdapter extends BaseAiClientConfigAdapter {
15
15
  : {};
16
16
  Object.assign(env, this.envProjection(profile));
17
17
  settings.env = env;
18
- settings.model = profile.model;
18
+ if (profile.model)
19
+ settings.model = profile.model;
19
20
  return new Map([[this.settingsPath, stringifyJson(settings)]]);
20
21
  }
21
22
  async verifyProjection(profile) {
@@ -24,7 +25,7 @@ class JsonEnvAdapter extends BaseAiClientConfigAdapter {
24
25
  const env = settings.env;
25
26
  const expected = this.envProjection(profile);
26
27
  const ok = env !== undefined
27
- && settings.model === profile.model
28
+ && (!profile.model || settings.model === profile.model)
28
29
  && Object.entries(expected).every(([key, value]) => env[key] === value);
29
30
  return ok ? { ok: true } : {
30
31
  ok: false,