@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.
- package/config/cloud.json +1 -1
- package/config/main.json +36 -2
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +3 -0
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +1 -1
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +2 -1
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +101 -32
- package/dist/api/ai-gateway/connect/DeviceCodeProtocol.d.ts +80 -0
- package/dist/api/ai-gateway/connect/DeviceCodeProtocol.js +3 -0
- package/dist/api/ai-gateway/connect/FileSessionImportAdapter.d.ts +38 -0
- package/dist/api/ai-gateway/connect/FileSessionImportAdapter.js +63 -0
- package/dist/api/ai-gateway/connect/LoopbackAuthorizationCallbackReceiver.d.ts +24 -0
- package/dist/api/ai-gateway/connect/LoopbackAuthorizationCallbackReceiver.js +136 -0
- package/dist/api/ai-gateway/connect/OAuthConnectAdapter.d.ts +2 -0
- package/dist/api/ai-gateway/connect/OAuthConnectAdapter.js +2 -1
- package/dist/api/ai-gateway/connect/OAuthIntegrationRegistry.d.ts +17 -13
- package/dist/api/ai-gateway/connect/OAuthIntegrationRegistry.js +52 -19
- package/dist/api/ai-gateway/connect/OpenAiSubscriptionSessionImportAdapter.d.ts +4 -28
- package/dist/api/ai-gateway/connect/OpenAiSubscriptionSessionImportAdapter.js +7 -58
- package/dist/api/ai-gateway/connect/ProviderAuthorizationProfiles.d.ts +4 -0
- package/dist/api/ai-gateway/connect/ProviderAuthorizationProfiles.js +116 -0
- package/dist/api/ai-gateway/connect/SessionImportProfiles.d.ts +10 -0
- package/dist/api/ai-gateway/connect/SessionImportProfiles.js +112 -0
- package/dist/api/ai-gateway/connect/index.d.ts +125 -23
- package/dist/api/ai-gateway/connect/index.js +1277 -260
- package/dist/api/ai-gateway/models/PodModelSelectionRepository.d.ts +4 -1
- package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +154 -19
- package/dist/api/ai-gateway/providers/DeepSeekRuntimeAdapter.js +10 -6
- package/dist/api/ai-gateway/providers/ModelsDevCatalog.d.ts +7 -0
- package/dist/api/ai-gateway/providers/ModelsDevCatalog.js +25 -1
- package/dist/api/ai-gateway/providers/OfferingAuthorization.d.ts +24 -0
- package/dist/api/ai-gateway/providers/OfferingAuthorization.js +37 -0
- package/dist/api/ai-gateway/providers/OpenAiRuntimeAdapter.d.ts +1 -1
- package/dist/api/ai-gateway/providers/OpenAiRuntimeAdapter.js +5 -3
- package/dist/api/ai-gateway/providers/ProviderRegistry.d.ts +6 -3
- package/dist/api/ai-gateway/providers/ProviderRegistry.js +55 -16
- package/dist/api/ai-gateway/providers/ProviderRuntimeAdapter.d.ts +2 -0
- package/dist/api/ai-gateway/providers/ProviderRuntimeAdapter.js +37 -4
- package/dist/api/ai-gateway/providers/ProviderRuntimeRegistry.js +7 -0
- package/dist/api/ai-gateway/quota/SubscriptionQuotaAdapters.js +2 -1
- package/dist/api/ai-gateway/routing/ModelRouter.js +3 -1
- package/dist/api/container/common.js +30 -40
- package/dist/api/container/index.js +6 -2
- package/dist/api/container/routes.js +5 -22
- package/dist/api/handlers/AiClientConfigurationHandler.js +39 -1
- package/dist/api/handlers/AiGatewayManagementHandler.d.ts +3 -0
- package/dist/api/handlers/AiGatewayManagementHandler.js +173 -58
- package/dist/api/service/AiClientConfigurationService.d.ts +4 -15
- package/dist/api/service/AiClientConfigurationService.js +17 -64
- package/dist/cli/commands/start.js +2 -0
- package/dist/components/components.jsonld +1 -0
- package/dist/components/context.jsonld +80 -0
- package/dist/http/InternalPodDataHttpHandler.d.ts +3 -0
- package/dist/http/InternalPodDataHttpHandler.js +13 -2
- package/dist/http/InternalPodDataHttpHandler.jsonld +12 -0
- package/dist/identity/oidc/RememberedClientPromptFactory.d.ts +1 -1
- package/dist/identity/oidc/RememberedClientPromptFactory.js +5 -2
- package/dist/identity/oidc/RememberedConsentHandler.js +1 -1
- package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +1 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.js +26 -0
- package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +4 -0
- package/dist/identity/oidc/SessionBoundIdentityProviderFactory.d.ts +10 -0
- package/dist/identity/oidc/SessionBoundIdentityProviderFactory.js +21 -0
- package/dist/identity/oidc/SessionBoundIdentityProviderFactory.jsonld +172 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -2
- package/dist/runtime/component-parameter-keys.js +18 -2
- package/dist/storage/accessors/MixDataAccessor.js +6 -2
- package/dist/storage/rdf/PostgresRdfEngine.js +10 -2
- package/dist/storage/rdf/RdfContentTypes.js +4 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.d.ts +3 -0
- package/node_modules/@undefineds.co/ai-connections/dist/AiClientConfigurationSection.js +13 -16
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsList.js +1 -3
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.d.ts +2 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +2 -2
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.d.ts +4 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsPanel.js +127 -57
- package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.d.ts +6 -4
- package/node_modules/@undefineds.co/ai-connections/dist/AiCredentialPoolSection.js +436 -83
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.d.ts +3 -2
- package/node_modules/@undefineds.co/ai-connections/dist/AiGatewayKeysSection.js +212 -76
- package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.d.ts +6 -4
- package/node_modules/@undefineds.co/ai-connections/dist/AiProviderCard.js +58 -41
- package/node_modules/@undefineds.co/ai-connections/dist/AiQuotaCard.d.ts +4 -1
- package/node_modules/@undefineds.co/ai-connections/dist/AiQuotaCard.js +63 -4
- package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.d.ts +46 -6
- package/node_modules/@undefineds.co/ai-connections/dist/ai-connections-client.js +143 -14
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.d.ts +19 -3
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/base-adapter.js +45 -7
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/codex.d.ts +7 -1
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/codex.js +305 -40
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/index.cjs +383 -50
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/json-env-adapter.js +3 -2
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/pi.js +12 -3
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/transaction.d.ts +2 -0
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/transaction.js +39 -4
- package/node_modules/@undefineds.co/ai-connections/dist/client-config/types.d.ts +8 -1
- package/node_modules/@undefineds.co/ai-connections/dist/client-visuals.d.ts +2 -0
- package/node_modules/@undefineds.co/ai-connections/dist/client-visuals.js +14 -0
- package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +2 -2
- package/node_modules/@undefineds.co/ai-connections/dist/controller.js +216 -57
- package/node_modules/@undefineds.co/ai-connections/dist/index.d.ts +3 -1
- package/node_modules/@undefineds.co/ai-connections/dist/index.js +16 -2
- package/node_modules/@undefineds.co/ai-connections/dist/provider-visuals.js +6 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/execution/ldp-executor.js +27 -6
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.js +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/query-builders/select-query-builder.js +2 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/sparql/builder/update-builder.js +12 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/builder.js +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/array.js +8 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/default.js +9 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/core/triple/handlers/inline.js +3 -3
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/execution/ldp-executor.js +27 -6
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.js +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/query-builders/select-query-builder.js +2 -2
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/sparql/builder/update-builder.js +12 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/builder.js +1 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/array.js +8 -0
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/default.js +9 -1
- package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/triple/handlers/inline.js +3 -3
- package/node_modules/@undefineds.co/extension-sdk/dist/web.d.ts +31 -0
- package/package.json +2 -2
- package/static/app/assets/{index-BikOyGPu.js → index-BCZVzMpj.js} +44 -44
- package/static/app/assets/inrupt-smoke.js +2 -2
- package/static/app/assets/main.css +1 -1
- package/static/app/assets/main.js +10 -10
- package/static/auth-callback/assets/{AiConfigContext-DrCH6ckh.js → AiConfigContext-DVkh8Kg2.js} +1 -1
- package/static/auth-callback/assets/{AiConfigPage-DVrlSSdA.js → AiConfigPage-HWVWpe-P.js} +1 -1
- package/static/auth-callback/assets/{DocumentProcessingPanel-_Md-v2R2.js → DocumentProcessingPanel-DDq-mH17.js} +1 -1
- package/static/auth-callback/assets/{IndexLifecyclePanel-BbWwe23H.js → IndexLifecyclePanel-Cx4cjrdP.js} +1 -1
- package/static/auth-callback/assets/{IndexSubjectPanel-upH9MjU-.js → IndexSubjectPanel-DzSw8M9j.js} +1 -1
- package/static/auth-callback/assets/{ModelAssignmentsPanel-CVGH4t4g.js → ModelAssignmentsPanel-BsRxGc2p.js} +1 -1
- package/static/auth-callback/assets/ModelsPage-DEe1lo6Q.js +10 -0
- package/static/auth-callback/assets/{NetworkPage-Dcbrb1fd.js → NetworkPage-DBiEh1Oz.js} +1 -1
- package/static/auth-callback/assets/{PaneListHeader-DrY-1wBq.js → PaneListHeader-lEEgjyN9.js} +1 -1
- package/static/auth-callback/assets/{SearchIndexingPanel-Dw25y53v.js → SearchIndexingPanel-CZCUUGt6.js} +1 -1
- package/static/auth-callback/assets/{StatusSubjectPanel-BHlz9rNF.js → StatusSubjectPanel-21qdueMr.js} +1 -1
- package/static/auth-callback/assets/{StatusWorkspace-BH7exQP5.js → StatusWorkspace-R8RKsqVz.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsPage-BYLKnM9f.js → SystemSettingsPage-BQ4e-4Wy.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsSubjectPanel-DT34LKku.js → SystemSettingsSubjectPanel-BopXD7SE.js} +1 -1
- package/static/auth-callback/assets/{activity-DdULRcSI.js → activity-DSfmTGfY.js} +1 -1
- package/static/auth-callback/assets/ai-connections-C03UWyPh.js +1 -0
- package/static/auth-callback/assets/{auth-callback-D7l2P25I.js → auth-callback-DleaitAS.js} +91 -91
- package/static/auth-callback/assets/auth-callback-vPSC2d2F.css +1 -0
- package/static/auth-callback/assets/{badge-j4zjFnmg.js → badge-B0aLu1sl.js} +1 -1
- package/static/auth-callback/assets/{download-DGXoks01.js → download-D_bFwdYJ.js} +1 -1
- package/static/auth-callback/assets/{external-link-CvbgXXaI.js → external-link-DRfjTaQu.js} +1 -1
- package/static/auth-callback/assets/{index-LRWvKJ2i.js → index-CG23cLYQ.js} +1 -1
- package/static/auth-callback/assets/{index-BOK3j1dz.js → index-CyV9ejwx.js} +1 -1
- package/static/auth-callback/assets/{index-browser-C1Qpe0g4.js → index-browser-D70paxIg.js} +1 -1
- package/static/auth-callback/assets/{key-round-CmgS5UdQ.js → key-round-BjV_mVYH.js} +1 -1
- package/static/auth-callback/assets/{rotate-ccw-c4CSG6Rq.js → rotate-ccw-CKoOAfse.js} +1 -1
- package/static/auth-callback/assets/{skeleton-ZPMTiRSM.js → skeleton-BZNjZaHx.js} +1 -1
- package/static/auth-callback/assets/{waypoints-xjyBflU5.js → waypoints-DoaJVzZv.js} +1 -1
- package/static/auth-callback/assets/{workspace-layout-B9nj6F5J.js → workspace-layout-CaP46_ZR.js} +1 -1
- package/static/auth-callback/auth-callback.html +2 -2
- package/static/dashboard/assets/{AiConfigContext-BWOOSYNr.js → AiConfigContext-BSrR27yD.js} +1 -1
- package/static/dashboard/assets/{AiConfigPage-CG2kQ9Wy.js → AiConfigPage-D65Z2LET.js} +1 -1
- package/static/dashboard/assets/{DocumentProcessingPanel-B4VXajfj.js → DocumentProcessingPanel-DyTe7Nei.js} +1 -1
- package/static/dashboard/assets/{IndexLifecyclePanel-CWCy0CC8.js → IndexLifecyclePanel-fK8ZGlrL.js} +1 -1
- package/static/dashboard/assets/{IndexSubjectPanel-CSgyQnG_.js → IndexSubjectPanel-CZPGor7v.js} +1 -1
- package/static/dashboard/assets/{ModelAssignmentsPanel-CdGIGidT.js → ModelAssignmentsPanel-CfYn5LMQ.js} +1 -1
- package/static/dashboard/assets/ModelsPage-djlJsghe.js +25 -0
- package/static/dashboard/assets/{NetworkPage-COia9NCH.js → NetworkPage-Coj6GnPl.js} +1 -1
- package/static/dashboard/assets/{PaneListHeader-SZWyr602.js → PaneListHeader-CUuFNSsZ.js} +1 -1
- package/static/dashboard/assets/{SearchIndexingPanel-D6PZtG6k.js → SearchIndexingPanel-CyJLioXS.js} +1 -1
- package/static/dashboard/assets/{StatusSubjectPanel-CxRD4kv0.js → StatusSubjectPanel-Cv26GSVC.js} +1 -1
- package/static/dashboard/assets/{StatusWorkspace-D3PfmY9-.js → StatusWorkspace-CDjFrsmB.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsPage-COg1MmMj.js → SystemSettingsPage-BuNOyNoa.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsSubjectPanel-BSVVB9Ol.js → SystemSettingsSubjectPanel-B2dasdez.js} +1 -1
- package/static/dashboard/assets/{activity-BJJx5cN-.js → activity-D1d2j_4Z.js} +1 -1
- package/static/dashboard/assets/ai-connections-C03UWyPh.js +1 -0
- package/static/dashboard/assets/{badge-elmrFSCj.js → badge-BeAAv6sp.js} +1 -1
- package/static/dashboard/assets/{dashboard-IvQrNmru.js → dashboard-BnGrUGv8.js} +68 -68
- package/static/dashboard/assets/dashboard-vPSC2d2F.css +1 -0
- package/static/dashboard/assets/{download-C3FH2KzG.js → download-DfFGiZRn.js} +1 -1
- package/static/dashboard/assets/{external-link-Bx7F0uKb.js → external-link-IsjU_LC9.js} +1 -1
- package/static/dashboard/assets/{index-liRoRIP1.js → index-BWQTucPe.js} +1 -1
- package/static/dashboard/assets/{index-M4-kWiGz.js → index-Dj75OjnW.js} +1 -1
- package/static/dashboard/assets/{key-round-CXg0bKAj.js → key-round-BJ_9wmQ_.js} +1 -1
- package/static/dashboard/assets/{rotate-ccw-yxJKOBrX.js → rotate-ccw-D3JGiFbR.js} +1 -1
- package/static/dashboard/assets/{skeleton-x9jNGrQT.js → skeleton-CC4nc-z-.js} +1 -1
- package/static/dashboard/assets/{waypoints-CTKeRsGp.js → waypoints-h7sqFqiG.js} +1 -1
- package/static/dashboard/assets/{workspace-layout--YkgkS38.js → workspace-layout-ZAPTOHhs.js} +1 -1
- package/static/dashboard/dashboard.html +2 -2
- package/static/settings/assets/{AiConfigContext-CelxXFcQ.js → AiConfigContext-lGkWpvKP.js} +1 -1
- package/static/settings/assets/{AiConfigPage-BaP-EovM.js → AiConfigPage-BbZWIBZq.js} +1 -1
- package/static/settings/assets/{DocumentProcessingPanel-DzLZluFv.js → DocumentProcessingPanel-DvYdjT-V.js} +1 -1
- package/static/settings/assets/{IndexLifecyclePanel-Bzj1-_oI.js → IndexLifecyclePanel-W5OGQZj7.js} +1 -1
- package/static/settings/assets/{IndexSubjectPanel-B263QLUh.js → IndexSubjectPanel-g_Aa5soq.js} +1 -1
- package/static/settings/assets/{ModelAssignmentsPanel-BAdT4UR0.js → ModelAssignmentsPanel-COlMBKXj.js} +1 -1
- package/static/settings/assets/ModelsPage-mApKi-aw.js +10 -0
- package/static/settings/assets/{NetworkPage-BK6a7l4g.js → NetworkPage-BDT05JUq.js} +1 -1
- package/static/settings/assets/{PaneListHeader-BAS2ICPq.js → PaneListHeader-CByhNpMG.js} +1 -1
- package/static/settings/assets/{SearchIndexingPanel-Cb_p-NgW.js → SearchIndexingPanel-BFAyzXls.js} +1 -1
- package/static/settings/assets/{StatusSubjectPanel-CcjqUua6.js → StatusSubjectPanel-Bz_XZ3VU.js} +1 -1
- package/static/settings/assets/{StatusWorkspace-7Ibty3_k.js → StatusWorkspace-CeVypHcW.js} +1 -1
- package/static/settings/assets/{SystemSettingsPage-DPSz4dg4.js → SystemSettingsPage-Dh7cQIfx.js} +1 -1
- package/static/settings/assets/{SystemSettingsSubjectPanel-Cb4HkKT5.js → SystemSettingsSubjectPanel-CSnpciO0.js} +1 -1
- package/static/settings/assets/{activity-Qhsr5Pxu.js → activity-DXz3EfFN.js} +1 -1
- package/static/settings/assets/ai-connections-C03UWyPh.js +1 -0
- package/static/settings/assets/{badge-B1vf2qCl.js → badge-1ZA144i4.js} +1 -1
- package/static/settings/assets/{download-CYNt0aZ-.js → download-CUHimlYa.js} +1 -1
- package/static/settings/assets/{external-link-ClKb39NT.js → external-link-BYHSVj2Z.js} +1 -1
- package/static/settings/assets/{index-y8ew3MLM.js → index-DKPDMdW5.js} +1 -1
- package/static/settings/assets/{index-browser-DJhvgpNI.js → index-browser-BaEFz0e0.js} +1 -1
- package/static/settings/assets/{index-BgiIDSch.js → index-viSgSvbO.js} +1 -1
- package/static/settings/assets/{key-round-5d-DrJz-.js → key-round-COByPOg8.js} +1 -1
- package/static/settings/assets/{rotate-ccw-CHZY7kS5.js → rotate-ccw-BC2EN6cE.js} +1 -1
- package/static/settings/assets/{settings-D5VRWyx6.js → settings-DLH9XUoT.js} +86 -86
- package/static/settings/assets/settings-vPSC2d2F.css +1 -0
- package/static/settings/assets/{skeleton-FNabNxNE.js → skeleton-B0-FOhKL.js} +1 -1
- package/static/settings/assets/{waypoints-BnL_8ljo.js → waypoints-DGMsvqWo.js} +1 -1
- package/static/settings/assets/{workspace-layout-D5q3F59S.js → workspace-layout-B0SVrJIu.js} +1 -1
- package/static/settings/settings.html +2 -2
- package/static/auth-callback/assets/ModelsPage-BpkVFKbf.js +0 -10
- package/static/auth-callback/assets/ai-connections-Bm52UO7Y.js +0 -1
- package/static/auth-callback/assets/auth-callback-01yz-UDU.css +0 -1
- package/static/dashboard/assets/ModelsPage-Chd5UK0D.js +0 -25
- package/static/dashboard/assets/ai-connections-Bm52UO7Y.js +0 -1
- package/static/dashboard/assets/dashboard-01yz-UDU.css +0 -1
- package/static/settings/assets/ModelsPage-C0NFa1zq.js +0 -10
- package/static/settings/assets/ai-connections-Bm52UO7Y.js +0 -1
- 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
|
-
|
|
182
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
472
|
+
const current = new Map;
|
|
415
473
|
for (const file of state.files) {
|
|
416
474
|
await this.rejectSymlink(file.path);
|
|
417
|
-
|
|
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
|
|
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
|
-
|
|
519
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
546
|
-
|
|
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
|
|
553
|
-
const
|
|
554
|
-
const
|
|
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.
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
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
|
|
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
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|