@undefineds.co/xpod 0.4.14 → 0.4.15
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 +0 -3
- package/config/local.json +0 -3
- package/config/xpod.base.json +0 -24
- package/dist/api/ai-config/AiConfigStore.d.ts +2 -2
- package/dist/api/ai-config/AiConfigStore.js +4 -1
- package/dist/api/ai-gateway/auth/CallerPodAccess.d.ts +2 -5
- package/dist/api/ai-gateway/auth/CallerPodAccess.js +3 -42
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.d.ts +2 -1
- package/dist/api/ai-gateway/auth/GatewayApiKeyAuthenticator.js +1 -1
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.d.ts +3 -8
- package/dist/api/ai-gateway/auth/PodGatewayAccessKeyRepository.js +9 -32
- package/dist/api/ai-gateway/connect/index.d.ts +3 -3
- package/dist/api/ai-gateway/connect/index.js +9 -38
- package/dist/api/ai-gateway/errors.js +2 -1
- package/dist/api/ai-gateway/models/PodModelSelectionRepository.d.ts +3 -3
- package/dist/api/ai-gateway/models/PodModelSelectionRepository.js +7 -11
- package/dist/api/ai-gateway/pod/HostedPodRoute.d.ts +27 -0
- package/dist/api/ai-gateway/pod/HostedPodRoute.js +47 -0
- package/dist/api/ai-gateway/pod/OwnerPodAccess.d.ts +79 -0
- package/dist/api/ai-gateway/pod/OwnerPodAccess.js +192 -0
- package/dist/api/ai-gateway/pod/PodInterfaceKeyStore.d.ts +52 -0
- package/dist/api/ai-gateway/pod/PodInterfaceKeyStore.js +61 -0
- package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.d.ts +3 -3
- package/dist/api/ai-gateway/quota/ProviderQuotaAdapter.js +7 -32
- package/dist/api/auth/ClientCredentialsAuthenticator.js +5 -28
- package/dist/api/auth/TokenEndpointRoute.d.ts +18 -0
- package/dist/api/auth/TokenEndpointRoute.js +36 -0
- package/dist/api/chatkit/pod-store.d.ts +4 -4
- package/dist/api/chatkit/pod-store.js +24 -95
- package/dist/api/container/common.js +36 -23
- package/dist/api/container/local.js +62 -29
- package/dist/api/container/routes.js +39 -7
- package/dist/api/container/types.d.ts +2 -2
- package/dist/api/handlers/AiGatewayManagementHandler.d.ts +6 -0
- package/dist/api/handlers/AiGatewayManagementHandler.js +17 -4
- package/dist/api/handlers/NetworkSettingsHandler.d.ts +7 -0
- package/dist/api/handlers/NetworkSettingsHandler.js +7 -0
- package/dist/api/handlers/PodSettingsHandler.d.ts +3 -3
- package/dist/api/handlers/PodSettingsHandler.js +3 -3
- package/dist/api/handlers/ProvisionHandler.d.ts +29 -0
- package/dist/api/handlers/ProvisionHandler.js +46 -0
- package/dist/api/handlers/ReachabilityHandler.js +3 -1
- package/dist/api/runtime.d.ts +9 -0
- package/dist/api/runtime.js +16 -3
- package/dist/cli/commands/start.d.ts +9 -2
- package/dist/cli/commands/start.js +15 -17
- package/dist/components/components.jsonld +1 -1
- package/dist/components/context.jsonld +5 -42
- package/dist/edge/EdgeNodeAgent.d.ts +10 -0
- package/dist/edge/EdgeNodeAgent.js +55 -0
- package/dist/edge/EdgeNodeAgent.jsonld +8 -0
- package/dist/edge/reachability/ReachabilitySessionService.js +4 -1
- package/dist/edge/reachability/RouteSetBuilder.d.ts +18 -1
- package/dist/edge/reachability/RouteSetBuilder.js +47 -0
- package/dist/edge/reachability/RouteSetBuilder.jsonld +29 -0
- package/dist/edge/reachability/types.d.ts +8 -2
- package/dist/http/SubgraphSparqlHttpHandler.d.ts +1 -25
- package/dist/http/SubgraphSparqlHttpHandler.js +0 -100
- package/dist/http/SubgraphSparqlHttpHandler.jsonld +0 -8
- package/dist/identity/drizzle/PodInterfaceKeyRepository.d.ts +33 -0
- package/dist/identity/drizzle/PodInterfaceKeyRepository.js +78 -0
- package/dist/identity/drizzle/db.d.ts +7 -0
- package/dist/identity/drizzle/db.js +30 -0
- package/dist/identity/drizzle/schema.pg.d.ts +9 -0
- package/dist/identity/drizzle/schema.pg.js +16 -1
- package/dist/identity/drizzle/schema.sqlite.d.ts +106 -0
- package/dist/identity/drizzle/schema.sqlite.js +16 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +3 -5
- package/dist/runtime/GatewayAdminProxyAuth.d.ts +1 -16
- package/dist/runtime/GatewayAdminProxyAuth.js +2 -71
- package/dist/runtime/Proxy.d.ts +10 -1
- package/dist/runtime/Proxy.js +95 -28
- package/dist/runtime/bootstrap.d.ts +10 -0
- package/dist/runtime/bootstrap.js +24 -0
- package/dist/runtime/host/node/NodeRuntimeHost.js +5 -3
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +2 -3
- package/dist/runtime/js-runtime.js +10 -3
- package/dist/runtime/lifecycle.js +5 -4
- package/dist/runtime/port-finder.d.ts +9 -13
- package/dist/runtime/port-finder.js +18 -37
- package/dist/tunnel/AutoTunnelProvider.d.ts +56 -0
- package/dist/tunnel/AutoTunnelProvider.js +181 -0
- package/dist/tunnel/index.d.ts +1 -0
- package/dist/tunnel/index.js +3 -1
- package/node_modules/@undefineds.co/ai-connections/dist/error-wording.js +7 -2
- package/node_modules/@undefineds.co/solid-sdk/dist/access-route.cjs +230 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/access-route.d.ts +76 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/access-route.js +115 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/index.cjs +89 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/index.d.ts +1 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/index.js +1 -0
- package/node_modules/@undefineds.co/solid-sdk/package.json +6 -1
- package/package.json +2 -2
- package/static/app/assets/inrupt-smoke.js +23 -23
- package/static/auth-callback/assets/{AiConfigContext-DCXpjzpj.js → AiConfigContext-Bx0HCAW5.js} +1 -1
- package/static/auth-callback/assets/{AiConfigPage-fe-xMIXa.js → AiConfigPage-ChQImMbm.js} +1 -1
- package/static/auth-callback/assets/{DocumentProcessingPanel-BGnkfMuU.js → DocumentProcessingPanel-D830H4SH.js} +1 -1
- package/static/auth-callback/assets/{IndexLifecyclePanel-42Jups0j.js → IndexLifecyclePanel-CnlkjMF8.js} +1 -1
- package/static/auth-callback/assets/{IndexSubjectPanel-CYyLOlQU.js → IndexSubjectPanel-CdEfg_CO.js} +1 -1
- package/static/auth-callback/assets/{ModelAssignmentsPanel-BOvUH4yr.js → ModelAssignmentsPanel-D-8A8dZx.js} +1 -1
- package/static/auth-callback/assets/{ModelsPage-DwyITxt7.js → ModelsPage-Cim7cubp.js} +3 -3
- package/static/auth-callback/assets/{NetworkPage-CG9RelJA.js → NetworkPage-CfK3x-pb.js} +2 -2
- package/static/auth-callback/assets/{PaneListHeader-yvd5ohs6.js → PaneListHeader-Bf_kEd4U.js} +1 -1
- package/static/auth-callback/assets/{SearchIndexingPanel-2_a6ezzj.js → SearchIndexingPanel-4kD4RBEQ.js} +1 -1
- package/static/auth-callback/assets/{StatusSubjectPanel-8qtVxnzC.js → StatusSubjectPanel-Bw9AiTSf.js} +1 -1
- package/static/auth-callback/assets/{StatusWorkspace-CBdRahCF.js → StatusWorkspace-C-301H-0.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsPage-BZJ6vWkg.js → SystemSettingsPage-BbWn-eG6.js} +1 -1
- package/static/auth-callback/assets/{SystemSettingsSubjectPanel-xm08UhXP.js → SystemSettingsSubjectPanel-DA4wvNSm.js} +1 -1
- package/static/auth-callback/assets/{XpodAiConnectionsPodStore-V6vgGfuw.js → XpodAiConnectionsPodStore-CohdyLoX.js} +1 -1
- package/static/auth-callback/assets/{activity-CeM7xly8.js → activity-CCsb9Oam.js} +1 -1
- package/static/auth-callback/assets/ai-connections-DexTPyfC.js +1 -0
- package/static/auth-callback/assets/{auth-callback-DTG8cSj7.js → auth-callback-Ds-c62W3.js} +5 -5
- package/static/auth-callback/assets/{badge-qJo2Zr4d.js → badge-CQm5OyzR.js} +1 -1
- package/static/auth-callback/assets/{collection-runtime-Bztsz31a.js → collection-runtime-DlAuU5TW.js} +1 -1
- package/static/auth-callback/assets/{dialog-D-h6x5x0.js → dialog-CnGzg5Ff.js} +1 -1
- package/static/auth-callback/assets/{download-C2ir4lmR.js → download-DwMONJvm.js} +1 -1
- package/static/auth-callback/assets/{external-link-THLDL4On.js → external-link-Bu5AGqCO.js} +1 -1
- package/static/auth-callback/assets/{index-BlVZb9A7.js → index-B0T-1YO8.js} +1 -1
- package/static/auth-callback/assets/{index-BRpv4yO3.js → index-C8QZf_4W.js} +1 -1
- package/static/auth-callback/assets/{index-D983R1FV.js → index-DwIlrIDG.js} +1 -1
- package/static/auth-callback/assets/{index-browser-3FvR0aaf.js → index-browser-lRsMcP_7.js} +1 -1
- package/static/auth-callback/assets/{key-round-C9U5j1Ly.js → key-round-BDMtU57P.js} +1 -1
- package/static/auth-callback/assets/{rotate-ccw-DP45cD4o.js → rotate-ccw-CoKIGCsQ.js} +1 -1
- package/static/auth-callback/assets/{skeleton-Dn4EiSCS.js → skeleton-CPoSWHDr.js} +1 -1
- package/static/auth-callback/assets/{waypoints-uqFQ3uEG.js → waypoints-BeYHxqHv.js} +1 -1
- package/static/auth-callback/assets/{workspace-layout-DLYK0Wwu.js → workspace-layout-xA5YxiK3.js} +1 -1
- package/static/auth-callback/auth-callback.html +1 -1
- package/static/dashboard/assets/{AiConfigContext-DJcMECNw.js → AiConfigContext-rWx4TyiH.js} +1 -1
- package/static/dashboard/assets/{AiConfigPage-BvE7dOTy.js → AiConfigPage-StVJpDFT.js} +1 -1
- package/static/dashboard/assets/{DocumentProcessingPanel-D9780krf.js → DocumentProcessingPanel-DJ5yul2O.js} +1 -1
- package/static/dashboard/assets/{IndexLifecyclePanel-SjsUcnbM.js → IndexLifecyclePanel-CpjtgezK.js} +1 -1
- package/static/dashboard/assets/{IndexSubjectPanel-C-ym2Tqi.js → IndexSubjectPanel-BVKcot1_.js} +1 -1
- package/static/dashboard/assets/{ModelAssignmentsPanel-BRjZ-arB.js → ModelAssignmentsPanel-BxVgWZPc.js} +1 -1
- package/static/dashboard/assets/{ModelsPage-BtTU6du3.js → ModelsPage-CECBwmeO.js} +3 -3
- package/static/dashboard/assets/{NetworkPage-CV9qpkDg.js → NetworkPage-zQgFscEW.js} +2 -2
- package/static/dashboard/assets/{PaneListHeader-KyPTTBsv.js → PaneListHeader-DIPe8pS6.js} +1 -1
- package/static/dashboard/assets/{SearchIndexingPanel-DolHGawg.js → SearchIndexingPanel-lalFJ3c8.js} +1 -1
- package/static/dashboard/assets/{StatusSubjectPanel-_YZvKiYi.js → StatusSubjectPanel-CpY_KU-G.js} +1 -1
- package/static/dashboard/assets/{StatusWorkspace-Dlbh_G40.js → StatusWorkspace-BwWCii3A.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsPage-Be7QSIoz.js → SystemSettingsPage-BKZxX-_n.js} +1 -1
- package/static/dashboard/assets/{SystemSettingsSubjectPanel-DBp0gja7.js → SystemSettingsSubjectPanel-BZrqb2C5.js} +1 -1
- package/static/dashboard/assets/{XpodAiConnectionsPodStore-ClpSjzpP.js → XpodAiConnectionsPodStore-Bbpx9dTN.js} +1 -1
- package/static/dashboard/assets/{activity-lsNbTXJu.js → activity-BJ15TLVP.js} +1 -1
- package/static/dashboard/assets/ai-connections-GG6qkTJt.js +1 -0
- package/static/dashboard/assets/{badge-CVatKjaG.js → badge-BBX5FlaB.js} +1 -1
- package/static/dashboard/assets/{collection-runtime-Ec4UnY28.js → collection-runtime-UBbHBzdf.js} +1 -1
- package/static/dashboard/assets/{dashboard-BEzsRsvF.js → dashboard-7Y6BHyV9.js} +62 -62
- package/static/dashboard/assets/{dialog-BuyelQ4V.js → dialog-QrOJ-hB0.js} +1 -1
- package/static/dashboard/assets/{download-B4Nf678F.js → download-DK9x-VOP.js} +1 -1
- package/static/dashboard/assets/{external-link-PYv9CvKL.js → external-link-Cnkn87mJ.js} +1 -1
- package/static/dashboard/assets/{index-C9XPE-mI.js → index-B1z84it5.js} +1 -1
- package/static/dashboard/assets/{index-awr7ulur.js → index-DzjauX3N.js} +1 -1
- package/static/dashboard/assets/{index-Cx69bTNh.js → index-cIa8_efI.js} +1 -1
- package/static/dashboard/assets/{key-round-VsY80U1Z.js → key-round-BoS6UqKz.js} +1 -1
- package/static/dashboard/assets/{rotate-ccw-Cz1K4vE9.js → rotate-ccw-BeWV76i0.js} +1 -1
- package/static/dashboard/assets/{skeleton-BhNv_8tO.js → skeleton-CypcJL07.js} +1 -1
- package/static/dashboard/assets/{waypoints-DYS9XFYV.js → waypoints-zHOOqfhi.js} +1 -1
- package/static/dashboard/assets/{workspace-layout-OZJAFx5V.js → workspace-layout-DZ9JuxnM.js} +1 -1
- package/static/dashboard/dashboard.html +1 -1
- package/static/settings/assets/{AiConfigContext-B8JS3MXF.js → AiConfigContext-D3b1l_Kw.js} +1 -1
- package/static/settings/assets/{AiConfigPage-C3Zl8LUl.js → AiConfigPage-DYKw-atP.js} +1 -1
- package/static/settings/assets/{DocumentProcessingPanel-DpDZAQvf.js → DocumentProcessingPanel-DguRfsIV.js} +1 -1
- package/static/settings/assets/{IndexLifecyclePanel-Ec1XF88t.js → IndexLifecyclePanel-B85qJJA4.js} +1 -1
- package/static/settings/assets/{IndexSubjectPanel-BulT9BmO.js → IndexSubjectPanel-CsN7IxaV.js} +1 -1
- package/static/settings/assets/{ModelAssignmentsPanel-BcGSmYjb.js → ModelAssignmentsPanel-DEp9byI6.js} +1 -1
- package/static/settings/assets/{ModelsPage-CvTCKy1T.js → ModelsPage-CzTnPs_G.js} +3 -3
- package/static/settings/assets/{NetworkPage-DQF5u9Bn.js → NetworkPage-BDVnmUU0.js} +2 -2
- package/static/settings/assets/{PaneListHeader-D40hqcVl.js → PaneListHeader-Cpe39N76.js} +1 -1
- package/static/settings/assets/{SearchIndexingPanel-iCUPOx_0.js → SearchIndexingPanel-DA9w8lLL.js} +1 -1
- package/static/settings/assets/{StatusSubjectPanel-E--ZHSql.js → StatusSubjectPanel-BU0Aq0CS.js} +1 -1
- package/static/settings/assets/{StatusWorkspace-YjDAx1II.js → StatusWorkspace-D1AC1i5N.js} +1 -1
- package/static/settings/assets/{SystemSettingsPage-CDdV3mC9.js → SystemSettingsPage-ChKvOioO.js} +1 -1
- package/static/settings/assets/{SystemSettingsSubjectPanel-Br8WazDq.js → SystemSettingsSubjectPanel-DyTpOnD2.js} +1 -1
- package/static/settings/assets/{XpodAiConnectionsPodStore-CLr38P_Z.js → XpodAiConnectionsPodStore-VU5Bsz1a.js} +1 -1
- package/static/settings/assets/{activity-C5DAHHTE.js → activity-DGpLfsQB.js} +1 -1
- package/static/settings/assets/ai-connections-DexTPyfC.js +1 -0
- package/static/settings/assets/{badge-D8eIyxKt.js → badge-_hCrrWRP.js} +1 -1
- package/static/settings/assets/{collection-runtime-DLc9zil8.js → collection-runtime-E40TZ8Cl.js} +1 -1
- package/static/settings/assets/{dialog-CyuR7Va4.js → dialog-DTr2JkHn.js} +1 -1
- package/static/settings/assets/{download-c0oxZrCz.js → download-B_D21DDX.js} +1 -1
- package/static/settings/assets/{external-link-D3Z34fP_.js → external-link-BATYaM13.js} +1 -1
- package/static/settings/assets/{index-CSr3-2Cj.js → index-BAlAVi-J.js} +1 -1
- package/static/settings/assets/{index-ComidxAm.js → index-Ckh4h9jj.js} +1 -1
- package/static/settings/assets/{index-CKGbRFlc.js → index-DOiwLF4B.js} +1 -1
- package/static/settings/assets/{index-browser-CAscEbae.js → index-browser-DFFWN3Kr.js} +1 -1
- package/static/settings/assets/{key-round-DyjLmXuB.js → key-round-ZTVU_1O1.js} +1 -1
- package/static/settings/assets/{rotate-ccw-679HF_ec.js → rotate-ccw-DDdw8PTU.js} +1 -1
- package/static/settings/assets/{settings-DKknqu4C.js → settings-CyLnISOv.js} +62 -62
- package/static/settings/assets/{skeleton-CkopNkPB.js → skeleton-DEzA3hc2.js} +1 -1
- package/static/settings/assets/{waypoints-CrqfAftE.js → waypoints-BSdfNvEb.js} +1 -1
- package/static/settings/assets/{workspace-layout-UN6c4qTG.js → workspace-layout-BOQnl_3d.js} +1 -1
- package/static/settings/settings.html +1 -1
- package/dist/api/ai-gateway/pod/HostedPodDataAccess.d.ts +0 -31
- package/dist/api/ai-gateway/pod/HostedPodDataAccess.js +0 -330
- package/dist/http/InternalPodDataHttpHandler.d.ts +0 -57
- package/dist/http/InternalPodDataHttpHandler.js +0 -551
- package/dist/http/InternalPodDataHttpHandler.jsonld +0 -243
- package/static/auth-callback/assets/ai-connections-C6yaE6WQ.js +0 -1
- package/static/dashboard/assets/ai-connections-CXp5gd1J.js +0 -1
- package/static/settings/assets/ai-connections-C6yaE6WQ.js +0 -1
|
@@ -22,6 +22,7 @@ const store_1 = require("../runs/store");
|
|
|
22
22
|
const schema_3 = require("../tasks/schema");
|
|
23
23
|
const store_2 = require("../tasks/store");
|
|
24
24
|
const TaskAuthBinding_1 = require("../tasks/TaskAuthBinding");
|
|
25
|
+
const OwnerPodAccess_1 = require("../ai-gateway/pod/OwnerPodAccess");
|
|
25
26
|
const AuthContext_1 = require("../auth/AuthContext");
|
|
26
27
|
const provider_1 = require("../../ai/schema/provider");
|
|
27
28
|
const model_1 = require("../../ai/schema/model");
|
|
@@ -60,7 +61,7 @@ class PodChatKitStore {
|
|
|
60
61
|
this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
61
62
|
// Track recently created message IDs to avoid SELECT cache timing issues
|
|
62
63
|
this.recentlyCreatedIds = new Set();
|
|
63
|
-
this.
|
|
64
|
+
this.podAccess = options.podAccess;
|
|
64
65
|
this.credentialSecretDecoder = options.credentialSecretDecoder ?? AiCredentialSecret_1.defaultAiCredentialSecretDecoder;
|
|
65
66
|
this.deployment = options.deployment;
|
|
66
67
|
this.serverGroupReconcilerService = options.serverGroupReconcilerService;
|
|
@@ -92,110 +93,38 @@ class PodChatKitStore {
|
|
|
92
93
|
return context._cachedDb;
|
|
93
94
|
}
|
|
94
95
|
const auth = context.auth;
|
|
95
|
-
if (!auth || !(0, AuthContext_1.isSolidAuth)(auth)) {
|
|
96
|
+
if (!auth || !(0, AuthContext_1.isSolidAuth)(auth) || !auth.webId) {
|
|
96
97
|
this.logger.warn('No valid solid auth in context, cannot access Pod');
|
|
97
98
|
return null;
|
|
98
99
|
}
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (auth.tokenType === 'DPoP') {
|
|
103
|
-
this.logger.warn('Using DPoP access token without proof key; Pod access may fail if issuer enforces DPoP proof');
|
|
104
|
-
}
|
|
105
|
-
this.logger.info(`[getDb] Using access token path for webId: ${auth.webId}`);
|
|
106
|
-
const authFetch = this.createAccessTokenFetch(auth.accessToken, auth.tokenType);
|
|
107
|
-
const db = (0, drizzle_solid_1.drizzle)({ fetch: authFetch, info: { webId: auth.webId, isLoggedIn: true } }, { schema });
|
|
108
|
-
this.logger.info(`Initializing tables for Pod (access token): ${auth.webId}`);
|
|
109
|
-
try {
|
|
110
|
-
await db.init(schema_1.Chat, schema_1.Thread, schema_1.Message, schema_2.Run, schema_2.RunStep, schema_3.Task, config_1.AIConfig, tables_1.Credential);
|
|
111
|
-
this.logger.info('Tables initialized successfully');
|
|
112
|
-
}
|
|
113
|
-
catch (initError) {
|
|
114
|
-
this.logger.error(`Failed to init tables: ${initError}`);
|
|
115
|
-
}
|
|
116
|
-
context._cachedDb = db;
|
|
117
|
-
context._cachedFetch = authFetch;
|
|
118
|
-
context._cachedWebId = auth.webId;
|
|
119
|
-
this.ensurePodBaseUrlCache(context, db, auth.webId);
|
|
120
|
-
return db;
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
this.logger.error(`Failed to get Pod db with access token: ${error}`);
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (!auth.clientId || !auth.clientSecret) {
|
|
128
|
-
this.logger.warn('No accessToken and no valid client credentials in context, cannot access Pod');
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
// Fallback path: exchange client credentials for an access token directly.
|
|
132
|
-
this.logger.info(`[getDb] Using client credentials path for clientId: ${auth.clientId}`);
|
|
100
|
+
// One credential path for every caller: the owner's own Pod key, exchanged for a
|
|
101
|
+
// token this process can prove, or the caller's reusable token.
|
|
102
|
+
let podFetch;
|
|
133
103
|
try {
|
|
134
|
-
|
|
135
|
-
const webId = auth.webId ?? this.getWebId(context);
|
|
136
|
-
if (!webId) {
|
|
137
|
-
throw new Error('Missing webId for client credentials auth');
|
|
138
|
-
}
|
|
139
|
-
this.logger.info(`[getDb] Client credentials token acquired, webId: ${webId}`);
|
|
140
|
-
const db = (0, drizzle_solid_1.drizzle)({ fetch: this.createAccessTokenFetch(token.accessToken, token.tokenType), info: { webId, isLoggedIn: true } }, { schema });
|
|
141
|
-
const authFetch = this.createAccessTokenFetch(token.accessToken, token.tokenType);
|
|
142
|
-
this.logger.info(`Initializing tables for Pod: ${webId}`);
|
|
143
|
-
try {
|
|
144
|
-
await db.init(schema_1.Chat, schema_1.Thread, schema_1.Message, schema_2.Run, schema_2.RunStep, schema_3.Task, config_1.AIConfig, tables_1.Credential);
|
|
145
|
-
this.logger.info('Tables initialized successfully');
|
|
146
|
-
}
|
|
147
|
-
catch (initError) {
|
|
148
|
-
this.logger.error(`Failed to init tables: ${initError}`);
|
|
149
|
-
}
|
|
150
|
-
context._cachedDb = db;
|
|
151
|
-
context._cachedFetch = authFetch;
|
|
152
|
-
context._cachedWebId = webId;
|
|
153
|
-
this.ensurePodBaseUrlCache(context, db, webId);
|
|
154
|
-
context._cachedAccessToken = token.accessToken;
|
|
155
|
-
context._cachedTokenType = token.tokenType;
|
|
156
|
-
return db;
|
|
104
|
+
podFetch = await this.podAccess?.getPodFetch(auth.webId, { auth });
|
|
157
105
|
}
|
|
158
106
|
catch (error) {
|
|
159
|
-
this.logger.error(`Failed to
|
|
107
|
+
this.logger.error(`Failed to obtain Pod access for ${auth.webId}: ${error}`);
|
|
160
108
|
return null;
|
|
161
109
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
method: 'POST',
|
|
166
|
-
headers: {
|
|
167
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
168
|
-
},
|
|
169
|
-
body: new URLSearchParams({
|
|
170
|
-
grant_type: 'client_credentials',
|
|
171
|
-
client_id: clientId,
|
|
172
|
-
client_secret: clientSecret,
|
|
173
|
-
}),
|
|
174
|
-
});
|
|
175
|
-
if (!response.ok) {
|
|
176
|
-
throw new Error(`Client credentials token request failed: ${response.status} ${await response.text().catch(() => '')}`);
|
|
110
|
+
if (!podFetch) {
|
|
111
|
+
this.logger.warn(`No usable Pod credential for ${auth.webId}: ${(0, OwnerPodAccess_1.podAccessError)(auth.webId, auth)}`);
|
|
112
|
+
return null;
|
|
177
113
|
}
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
114
|
+
const db = (0, drizzle_solid_1.drizzle)({ fetch: podFetch, info: { webId: auth.webId, isLoggedIn: true } }, { schema });
|
|
115
|
+
this.logger.info(`Initializing tables for Pod: ${auth.webId}`);
|
|
116
|
+
try {
|
|
117
|
+
await db.init(schema_1.Chat, schema_1.Thread, schema_1.Message, schema_2.Run, schema_2.RunStep, schema_3.Task, config_1.AIConfig, tables_1.Credential);
|
|
118
|
+
this.logger.info('Tables initialized successfully');
|
|
181
119
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return
|
|
190
|
-
const headers = new Headers(init?.headers);
|
|
191
|
-
if (!headers.has('Authorization')) {
|
|
192
|
-
headers.set('Authorization', `${scheme} ${accessToken}`);
|
|
193
|
-
}
|
|
194
|
-
return fetch(input, {
|
|
195
|
-
...init,
|
|
196
|
-
headers,
|
|
197
|
-
});
|
|
198
|
-
};
|
|
120
|
+
catch (initError) {
|
|
121
|
+
this.logger.error(`Failed to init tables: ${initError}`);
|
|
122
|
+
}
|
|
123
|
+
context._cachedDb = db;
|
|
124
|
+
context._cachedFetch = podFetch;
|
|
125
|
+
context._cachedWebId = auth.webId;
|
|
126
|
+
this.ensurePodBaseUrlCache(context, db, auth.webId);
|
|
127
|
+
return db;
|
|
199
128
|
}
|
|
200
129
|
/**
|
|
201
130
|
* 从 context 获取 webId
|
|
@@ -27,7 +27,10 @@ const InvocationTokenCodec_1 = require("../ai-gateway/auth/InvocationTokenCodec"
|
|
|
27
27
|
const GatewayApiKeyAuthenticator_1 = require("../ai-gateway/auth/GatewayApiKeyAuthenticator");
|
|
28
28
|
const GatewayKeyLocatorCodec_1 = require("../ai-gateway/auth/GatewayKeyLocatorCodec");
|
|
29
29
|
const PodGatewayAccessKeyRepository_1 = require("../ai-gateway/auth/PodGatewayAccessKeyRepository");
|
|
30
|
-
const
|
|
30
|
+
const OwnerPodAccess_1 = require("../ai-gateway/pod/OwnerPodAccess");
|
|
31
|
+
const HostedPodRoute_1 = require("../ai-gateway/pod/HostedPodRoute");
|
|
32
|
+
const PodInterfaceKeyRepository_1 = require("../../identity/drizzle/PodInterfaceKeyRepository");
|
|
33
|
+
const PodInterfaceKeyStore_1 = require("../ai-gateway/pod/PodInterfaceKeyStore");
|
|
31
34
|
const AiGatewayService_1 = require("../ai-gateway/AiGatewayService");
|
|
32
35
|
const PlaintextCredentialVault_1 = require("../ai-gateway/credentials/PlaintextCredentialVault");
|
|
33
36
|
const AiCredentialSecretDecoder_1 = require("../ai-gateway/credentials/AiCredentialSecretDecoder");
|
|
@@ -139,10 +142,21 @@ function registerCommonServices(container) {
|
|
|
139
142
|
scopes: ['quota:write', 'usage:read', 'account:manage', 'network:read', 'network:write'],
|
|
140
143
|
});
|
|
141
144
|
}).singleton(),
|
|
142
|
-
|
|
143
|
-
return new
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
ownerPodAccess: (0, awilix_1.asFunction)(({ config, db }) => {
|
|
146
|
+
return new OwnerPodAccess_1.OwnerPodAccess({
|
|
147
|
+
keys: new PodInterfaceKeyStore_1.PodInterfaceKeyStore({
|
|
148
|
+
repository: new PodInterfaceKeyRepository_1.PodInterfaceKeyRepository(db),
|
|
149
|
+
vault: credentialVaultForConfig(config),
|
|
150
|
+
}),
|
|
151
|
+
tokenEndpoint: config.cssTokenEndpoint,
|
|
152
|
+
publicBaseUrl: config.solidBaseUrl,
|
|
153
|
+
route: (0, HostedPodRoute_1.resolveHostedPodRoute)({
|
|
154
|
+
canonicalBaseUrl: config.solidBaseUrl,
|
|
155
|
+
// API_HOST is the address the runtime bound its services to; XPOD_MAIN_PORT is the
|
|
156
|
+
// Gateway's. Both are read here, while the runtime still has its environment applied.
|
|
157
|
+
gatewayHost: process.env.API_HOST,
|
|
158
|
+
gatewayPort: process.env.XPOD_MAIN_PORT,
|
|
159
|
+
}),
|
|
146
160
|
});
|
|
147
161
|
}).singleton(),
|
|
148
162
|
invocationTokenCodec: (0, awilix_1.asFunction)(() => {
|
|
@@ -156,7 +170,7 @@ function registerCommonServices(container) {
|
|
|
156
170
|
});
|
|
157
171
|
}).singleton(),
|
|
158
172
|
gatewayAccessKeyRepository: (0, awilix_1.asFunction)((cradle) => {
|
|
159
|
-
const { config,
|
|
173
|
+
const { config, ownerPodAccess } = cradle;
|
|
160
174
|
return new PodGatewayAccessKeyRepository_1.PodGatewayAccessKeyRepository({
|
|
161
175
|
locatorCodec: new GatewayKeyLocatorCodec_1.AesGatewayKeyLocatorCodec({
|
|
162
176
|
active: {
|
|
@@ -165,7 +179,7 @@ function registerCommonServices(container) {
|
|
|
165
179
|
},
|
|
166
180
|
previous: config.gatewayPreviousLocatorSecrets,
|
|
167
181
|
}),
|
|
168
|
-
|
|
182
|
+
podAccess: ownerPodAccess,
|
|
169
183
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
170
184
|
});
|
|
171
185
|
}).singleton(),
|
|
@@ -180,9 +194,8 @@ function registerCommonServices(container) {
|
|
|
180
194
|
}).singleton(),
|
|
181
195
|
providerConnectService: (0, awilix_1.asFunction)((cradle) => {
|
|
182
196
|
const { config } = cradle;
|
|
183
|
-
const internalPodAccess = cradle.hostedPodDataAccess;
|
|
184
197
|
const credentialRepository = new connect_1.PodConnectedCredentialRepository({
|
|
185
|
-
|
|
198
|
+
podAccess: cradle.ownerPodAccess,
|
|
186
199
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
187
200
|
});
|
|
188
201
|
const vault = credentialVaultForConfig(config);
|
|
@@ -253,24 +266,24 @@ function registerCommonServices(container) {
|
|
|
253
266
|
systemProxy: config.edition === 'local' ? (0, provider_http_transport_1.discoverSystemProviderProxy)() : undefined,
|
|
254
267
|
})).singleton(),
|
|
255
268
|
gatewayCredentialStore: (0, awilix_1.asFunction)((cradle) => {
|
|
256
|
-
const {
|
|
269
|
+
const { ownerPodAccess } = cradle;
|
|
257
270
|
return new connect_1.PodConnectedCredentialRepository({
|
|
258
|
-
|
|
271
|
+
podAccess: ownerPodAccess,
|
|
259
272
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
260
273
|
});
|
|
261
274
|
}).singleton(),
|
|
262
275
|
podModelSelectionRepository: (0, awilix_1.asFunction)((cradle) => {
|
|
263
|
-
const {
|
|
276
|
+
const { ownerPodAccess } = cradle;
|
|
264
277
|
return new PodModelSelectionRepository_1.PodModelSelectionRepository({
|
|
265
|
-
|
|
278
|
+
podAccess: ownerPodAccess,
|
|
266
279
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
267
280
|
});
|
|
268
281
|
}).singleton(),
|
|
269
282
|
providerModelSelectionService: (0, awilix_1.asFunction)((cradle) => {
|
|
270
|
-
const { config, gatewayProviderRegistry,
|
|
283
|
+
const { config, gatewayProviderRegistry, ownerPodAccess, podModelSelectionRepository, providerModelsService, } = cradle;
|
|
271
284
|
return new ProviderModelSelectionService_1.ProviderModelSelectionService({
|
|
272
285
|
credentialRepository: new connect_1.PodConnectedCredentialRepository({
|
|
273
|
-
|
|
286
|
+
podAccess: ownerPodAccess,
|
|
274
287
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
275
288
|
}),
|
|
276
289
|
selectionRepository: podModelSelectionRepository,
|
|
@@ -346,14 +359,14 @@ function registerCommonServices(container) {
|
|
|
346
359
|
}).singleton(),
|
|
347
360
|
providerQuotaService: (0, awilix_1.asFunction)((cradle) => {
|
|
348
361
|
const { config } = cradle;
|
|
349
|
-
const
|
|
362
|
+
const podAccess = cradle.ownerPodAccess;
|
|
350
363
|
return new quota_1.ProviderQuotaService({
|
|
351
364
|
repository: new quota_1.PodQuotaSnapshotRepository({
|
|
352
|
-
|
|
365
|
+
podAccess,
|
|
353
366
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
354
367
|
}),
|
|
355
368
|
credentialRepository: new connect_1.PodConnectedCredentialRepository({
|
|
356
|
-
|
|
369
|
+
podAccess,
|
|
357
370
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
358
371
|
}),
|
|
359
372
|
vault: credentialVaultForConfig(config),
|
|
@@ -373,7 +386,7 @@ function registerCommonServices(container) {
|
|
|
373
386
|
}).singleton(),
|
|
374
387
|
providerModelsService: (0, awilix_1.asFunction)((cradle) => {
|
|
375
388
|
const { config } = cradle;
|
|
376
|
-
const
|
|
389
|
+
const podAccess = cradle.ownerPodAccess;
|
|
377
390
|
const registry = cradle.gatewayProviderRegistry;
|
|
378
391
|
const safeBaseUrls = (provider) => [
|
|
379
392
|
...registry.requireProvider(provider).safeBaseUrls,
|
|
@@ -381,7 +394,7 @@ function registerCommonServices(container) {
|
|
|
381
394
|
];
|
|
382
395
|
return new models_1.ProviderModelsService({
|
|
383
396
|
credentialRepository: new connect_1.PodConnectedCredentialRepository({
|
|
384
|
-
|
|
397
|
+
podAccess,
|
|
385
398
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
386
399
|
}),
|
|
387
400
|
vault: credentialVaultForConfig(config),
|
|
@@ -446,7 +459,7 @@ function registerCommonServices(container) {
|
|
|
446
459
|
providerCustomModelsService: (0, awilix_1.asFunction)((cradle) => {
|
|
447
460
|
return new models_1.ProviderCustomModelsService({
|
|
448
461
|
credentialRepository: new connect_1.PodConnectedCredentialRepository({
|
|
449
|
-
|
|
462
|
+
podAccess: cradle.ownerPodAccess,
|
|
450
463
|
podBaseUrlResolver: podBaseUrlResolver(cradle),
|
|
451
464
|
}),
|
|
452
465
|
embeddingModelPolicy: cradle.embeddingModelPolicy,
|
|
@@ -511,9 +524,9 @@ function registerCommonServices(container) {
|
|
|
511
524
|
});
|
|
512
525
|
}).singleton(),
|
|
513
526
|
// ChatKit 存储与服务
|
|
514
|
-
chatKitStore: (0, awilix_1.asFunction)(({ config, serverGroupReconcilerService }) => {
|
|
527
|
+
chatKitStore: (0, awilix_1.asFunction)(({ config, ownerPodAccess, serverGroupReconcilerService }) => {
|
|
515
528
|
return new chatkit_1.PodChatKitStore({
|
|
516
|
-
|
|
529
|
+
podAccess: ownerPodAccess,
|
|
517
530
|
serverGroupReconcilerService,
|
|
518
531
|
deployment: config.edition,
|
|
519
532
|
credentialSecretDecoder: (0, AiCredentialSecretDecoder_1.createAiCredentialSecretDecoder)({
|
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.registerLocalServices = registerLocalServices;
|
|
11
11
|
const awilix_1 = require("awilix");
|
|
12
12
|
const SubdomainClient_1 = require("../../subdomain/SubdomainClient");
|
|
13
|
+
const AutoTunnelProvider_1 = require("../../tunnel/AutoTunnelProvider");
|
|
13
14
|
const LocalTunnelProvider_1 = require("../../tunnel/LocalTunnelProvider");
|
|
14
15
|
const NgrokTunnelProvider_1 = require("../../tunnel/NgrokTunnelProvider");
|
|
15
16
|
const SakuraFrpTunnelProvider_1 = require("../../tunnel/SakuraFrpTunnelProvider");
|
|
@@ -49,40 +50,72 @@ function registerLocalServices(container) {
|
|
|
49
50
|
const activeCredential = activeTunnel.profile?.credentialEnvKey
|
|
50
51
|
? readCredential(process.env[activeTunnel.profile.credentialEnvKey])
|
|
51
52
|
: undefined;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
// Every provider the operator has credentials for is a candidate; which one
|
|
54
|
+
// can reach its control plane is a property of the network, so an implicit
|
|
55
|
+
// selection is settled by readiness instead of by list order. An explicitly
|
|
56
|
+
// pinned profile or provider still starts exactly that one - including when it
|
|
57
|
+
// has no credential, because then its own failure is the answer the operator
|
|
58
|
+
// needs rather than a silently absent provider.
|
|
59
|
+
const profileFor = (provider) => (config.tunnelProfiles ?? []).find((entry) => entry.provider === provider);
|
|
60
|
+
const profileCredential = (provider) => {
|
|
61
|
+
const key = profileFor(provider)?.credentialEnvKey;
|
|
62
|
+
return key ? readCredential(process.env[key]) : undefined;
|
|
63
|
+
};
|
|
64
|
+
const credentialFor = (provider) => (activeTunnelProvider === provider ? activeCredential : undefined) ?? profileCredential(provider);
|
|
65
|
+
const candidates = [
|
|
66
|
+
{
|
|
67
|
+
id: 'ngrok',
|
|
68
|
+
provider: 'ngrok',
|
|
69
|
+
configured: Boolean(credentialFor('ngrok') ?? ngrokAuthToken ?? ngrokUrl),
|
|
70
|
+
create: () => new NgrokTunnelProvider_1.NgrokTunnelProvider({
|
|
71
|
+
authtoken: credentialFor('ngrok') ?? ngrokAuthToken,
|
|
72
|
+
url: profileFor('ngrok')?.publicUrl ?? ngrokUrl,
|
|
73
|
+
ngrokPath,
|
|
74
|
+
}),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'cloudflare',
|
|
78
|
+
provider: 'cloudflare',
|
|
79
|
+
configured: Boolean(credentialFor('cloudflare') ?? cloudflareTunnelToken),
|
|
80
|
+
create: () => new LocalTunnelProvider_1.LocalTunnelProvider({
|
|
81
|
+
tunnelToken: (credentialFor('cloudflare') ?? cloudflareTunnelToken),
|
|
82
|
+
publicUrl: profileFor('cloudflare')?.publicUrl,
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'sakura_frp',
|
|
87
|
+
provider: 'sakura_frp',
|
|
88
|
+
configured: Boolean(credentialFor('sakura_frp') ?? sakuraTunnelToken),
|
|
89
|
+
create: () => new SakuraFrpTunnelProvider_1.SakuraFrpTunnelProvider({
|
|
90
|
+
token: (credentialFor('sakura_frp') ?? sakuraTunnelToken),
|
|
91
|
+
publicUrl: profileFor('sakura_frp')?.publicUrl,
|
|
92
|
+
frpcPath,
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
const pinnedProvider = config.tunnelActiveProfileId || config.tunnelProvider || process.env.XPOD_TUNNEL_PROVIDER
|
|
97
|
+
? activeTunnelProvider
|
|
98
|
+
: undefined;
|
|
99
|
+
const selected = pinnedProvider
|
|
100
|
+
? candidates.filter((candidate) => candidate.provider === pinnedProvider)
|
|
101
|
+
: candidates.filter((candidate) => candidate.configured);
|
|
102
|
+
if (selected.length === 1) {
|
|
103
|
+
const [only] = selected;
|
|
65
104
|
container.register({
|
|
66
|
-
localTunnelProvider: (0, awilix_1.asFunction)(()
|
|
67
|
-
return new LocalTunnelProvider_1.LocalTunnelProvider({
|
|
68
|
-
tunnelToken: (activeCredential ?? cloudflareTunnelToken),
|
|
69
|
-
publicUrl: activeTunnel.profile?.publicUrl,
|
|
70
|
-
});
|
|
71
|
-
}).singleton(),
|
|
105
|
+
localTunnelProvider: (0, awilix_1.asFunction)(only.create).singleton(),
|
|
72
106
|
});
|
|
73
|
-
console.log(
|
|
107
|
+
console.log(`[Local] Tunnel provider registered (${only.id} configured)`);
|
|
74
108
|
}
|
|
75
|
-
else if (
|
|
109
|
+
else if (selected.length > 1) {
|
|
76
110
|
container.register({
|
|
77
|
-
localTunnelProvider: (0, awilix_1.asFunction)(() => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}).singleton(),
|
|
111
|
+
localTunnelProvider: (0, awilix_1.asFunction)(() => new AutoTunnelProvider_1.AutoTunnelProvider({
|
|
112
|
+
candidates: selected.map((candidate) => ({
|
|
113
|
+
id: candidate.id,
|
|
114
|
+
provider: candidate.create(),
|
|
115
|
+
})),
|
|
116
|
+
})).singleton(),
|
|
84
117
|
});
|
|
85
|
-
console.log(
|
|
118
|
+
console.log(`[Local] Tunnel providers registered (auto: ${selected.map((candidate) => candidate.id).join(', ')})`);
|
|
86
119
|
}
|
|
87
120
|
// 2. 自适应 DNS 管理 (Self-Hosted DNS)
|
|
88
121
|
// 如果配置了 Cloudflare API Token 和 Base Domain,启用本地 DNS 管理
|
|
@@ -34,6 +34,7 @@ exports.resolveLocalStorageProviderBaseUrl = resolveLocalStorageProviderBaseUrl;
|
|
|
34
34
|
const EdgeNodeSignalHandler_1 = require("../handlers/EdgeNodeSignalHandler");
|
|
35
35
|
const ReachabilityHandler_1 = require("../handlers/ReachabilityHandler");
|
|
36
36
|
const NodeAccessResolver_1 = require("../../edge/reachability/NodeAccessResolver");
|
|
37
|
+
const RouteSetBuilder_1 = require("../../edge/reachability/RouteSetBuilder");
|
|
37
38
|
const NodeHandler_1 = require("../handlers/NodeHandler");
|
|
38
39
|
const ChatHandler_1 = require("../handlers/ChatHandler");
|
|
39
40
|
const SubdomainHandler_1 = require("../handlers/SubdomainHandler");
|
|
@@ -131,7 +132,7 @@ function registerSharedRoutes(container, server) {
|
|
|
131
132
|
const rdfStorageStatsService = container.resolve('rdfStorageStatsService');
|
|
132
133
|
const rdfEngine = container.resolve('rdfEngine', { allowUnregistered: true });
|
|
133
134
|
const rdfSearchIndexingService = container.resolve('rdfSearchIndexingService', { allowUnregistered: true });
|
|
134
|
-
const
|
|
135
|
+
const ownerPodAccess = container.resolve('ownerPodAccess');
|
|
135
136
|
const aiConnectionInvocationKeyIssuer = container.resolve('aiConnectionInvocationKeyIssuer');
|
|
136
137
|
const gatewayAccessKeyRepository = container.resolve('gatewayAccessKeyRepository', { allowUnregistered: true });
|
|
137
138
|
const providerConnectService = container.resolve('providerConnectService');
|
|
@@ -195,6 +196,7 @@ function registerSharedRoutes(container, server) {
|
|
|
195
196
|
providerModelSelectionService,
|
|
196
197
|
customModelsService: providerCustomModelsService,
|
|
197
198
|
gatewayAccessKeyRepository,
|
|
199
|
+
podInterfaceKeys: ownerPodAccess,
|
|
198
200
|
validateClientCredential: (apiKey) => container.resolve('authenticator').authenticate({
|
|
199
201
|
headers: { authorization: `Bearer ${apiKey}` },
|
|
200
202
|
method: 'POST',
|
|
@@ -214,15 +216,15 @@ function registerSharedRoutes(container, server) {
|
|
|
214
216
|
(0, PodSettingsHandler_1.registerPodSettingsRoutes)(server, {
|
|
215
217
|
podLookupRepository,
|
|
216
218
|
usageRepo: new UsageRepository_1.UsageRepository(container.resolve('db')),
|
|
217
|
-
aiConnectionStatusReader: new PodSettingsHandler_1.DrizzlePodAiConnectionsStatusReader(
|
|
219
|
+
aiConnectionStatusReader: new PodSettingsHandler_1.DrizzlePodAiConnectionsStatusReader(ownerPodAccess, config.edition),
|
|
218
220
|
});
|
|
219
221
|
const aiConfigStore = new AiConfigStore_1.DrizzlePodAiConfigStore({
|
|
220
|
-
|
|
222
|
+
podAccess: ownerPodAccess,
|
|
221
223
|
});
|
|
222
|
-
const ftsRebuildAvailable = Boolean(
|
|
223
|
-
const vectorRebuildAvailable = Boolean(
|
|
224
|
+
const ftsRebuildAvailable = Boolean(ownerPodAccess && rdfEngine?.indexTextSource);
|
|
225
|
+
const vectorRebuildAvailable = Boolean(ownerPodAccess && rdfSearchIndexingService && chatKitStore.createTrustedContext);
|
|
224
226
|
const rebuildFts = async (owner) => {
|
|
225
|
-
const trustedFetch = await
|
|
227
|
+
const trustedFetch = await ownerPodAccess.getPodFetch(owner.webId, { podBaseUrl: owner.podUrl });
|
|
226
228
|
if (!trustedFetch || !rdfEngine?.indexTextSource)
|
|
227
229
|
throw new Error('fts_rebuild_unavailable');
|
|
228
230
|
const result = await new PodSearchIndexRebuilder_1.PodSearchIndexRebuilder({
|
|
@@ -235,7 +237,7 @@ function registerSharedRoutes(container, server) {
|
|
|
235
237
|
throw new Error('fts_rebuild_incomplete');
|
|
236
238
|
};
|
|
237
239
|
const rebuildVector = async (owner) => {
|
|
238
|
-
const trustedFetch = await
|
|
240
|
+
const trustedFetch = await ownerPodAccess.getPodFetch(owner.webId, { podBaseUrl: owner.podUrl });
|
|
239
241
|
if (!trustedFetch || !rdfSearchIndexingService)
|
|
240
242
|
throw new Error('vector_rebuild_unavailable');
|
|
241
243
|
const context = await chatKitStore.createTrustedContext({ ...owner, fetch: trustedFetch });
|
|
@@ -523,6 +525,8 @@ function registerLocalRoutes(container, server) {
|
|
|
523
525
|
// SP 状态查询 (供 Linx 查询 SP 配置状态)
|
|
524
526
|
try {
|
|
525
527
|
const config = container.resolve('config');
|
|
528
|
+
const statusTunnelProvider = (container.resolve('localTunnelProvider', { allowUnregistered: true })
|
|
529
|
+
?? container.resolve('tunnelProvider', { allowUnregistered: true }));
|
|
526
530
|
(0, ProvisionHandler_1.registerProvisionStatusRoute)(server, {
|
|
527
531
|
cloudUrl: config.cloudApiEndpoint,
|
|
528
532
|
nodeId: config.nodeId,
|
|
@@ -538,7 +542,35 @@ function registerLocalRoutes(container, server) {
|
|
|
538
542
|
?? process.env.SAKURA_TOKEN,
|
|
539
543
|
cloudBaseUrl: config.oidcIssuer || config.cloudApiEndpoint,
|
|
540
544
|
provisionCode: process.env.XPOD_PROVISION_CODE ?? config.provisionCode,
|
|
545
|
+
// A configured tunnel only becomes a public route once its provider
|
|
546
|
+
// reports a proxy-ready endpoint; clients use this to decide whether a
|
|
547
|
+
// canonical request can leave the machine at all.
|
|
548
|
+
readPublicRoute: () => {
|
|
549
|
+
if (!statusTunnelProvider) {
|
|
550
|
+
return { configured: false, available: false };
|
|
551
|
+
}
|
|
552
|
+
const status = statusTunnelProvider.getStatus();
|
|
553
|
+
const endpoint = status.endpoint ?? statusTunnelProvider.getEndpoint();
|
|
554
|
+
return {
|
|
555
|
+
configured: true,
|
|
556
|
+
available: Boolean(status.connected),
|
|
557
|
+
...(statusTunnelProvider.name ? { provider: statusTunnelProvider.name } : {}),
|
|
558
|
+
...(endpoint ? { endpoint } : {}),
|
|
559
|
+
};
|
|
560
|
+
},
|
|
541
561
|
persistState: (0, ProvisionHandler_1.createLocalSetupProvisionStateWriter)(config.localSetupPath, config.localSetupProviderId),
|
|
562
|
+
// A client ranks access points by where it stands, so the node reports the
|
|
563
|
+
// paths it can prove: its canonical public route, plus the tunnel endpoint
|
|
564
|
+
// whenever a provider confirms one that is not simply the canonical URL.
|
|
565
|
+
readAccessRoutes: () => {
|
|
566
|
+
const status = statusTunnelProvider?.getStatus();
|
|
567
|
+
const publicUrl = process.env.XPOD_PUBLIC_URL ?? config.publicUrl ?? process.env.CSS_BASE_URL;
|
|
568
|
+
return (0, RouteSetBuilder_1.buildPublicAccessRoutes)({
|
|
569
|
+
canonicalUrl: publicUrl,
|
|
570
|
+
publicRouteAvailable: Boolean(status?.connected),
|
|
571
|
+
tunnelEndpoint: status?.endpoint ?? statusTunnelProvider?.getEndpoint(),
|
|
572
|
+
});
|
|
573
|
+
},
|
|
542
574
|
});
|
|
543
575
|
console.log('[Local] Provision status route registered (/provision/status)');
|
|
544
576
|
}
|
|
@@ -51,7 +51,7 @@ import type { ProviderRegistry as GatewayProviderRegistry } from '../ai-gateway/
|
|
|
51
51
|
import type { SessionAffinityStore } from '../ai-gateway/routing/SessionAffinityStore';
|
|
52
52
|
import type { AiConnectionsInvocationKeyIssuer } from '../ai-gateway/auth/AiConnectionsInvocationKeyIssuer';
|
|
53
53
|
import type { InvocationTokenCodec } from '../ai-gateway/auth/InvocationTokenCodec';
|
|
54
|
-
import type {
|
|
54
|
+
import type { OwnerPodAccess } from '../ai-gateway/pod/OwnerPodAccess';
|
|
55
55
|
import type { GatewayAccessKeyRepository } from '../ai-gateway/auth/GatewayApiKeyAuthenticator';
|
|
56
56
|
import type { PodModelSelectionRepository } from '../ai-gateway/models/PodModelSelectionRepository';
|
|
57
57
|
import type { ProviderModelSelectionService } from '../ai-gateway/models/ProviderModelSelectionService';
|
|
@@ -204,7 +204,7 @@ export interface ApiContainerCradle {
|
|
|
204
204
|
authenticator: Authenticator;
|
|
205
205
|
nodeRepo: EdgeNodeRepository;
|
|
206
206
|
serviceTokenRepo: ServiceTokenRepositoryPort;
|
|
207
|
-
|
|
207
|
+
ownerPodAccess: OwnerPodAccess;
|
|
208
208
|
invocationTokenCodec?: InvocationTokenCodec;
|
|
209
209
|
gatewayAccessKeyRepository?: GatewayAccessKeyRepository;
|
|
210
210
|
aiConnectionInvocationKeyIssuer?: AiConnectionsInvocationKeyIssuer;
|
|
@@ -6,6 +6,7 @@ import type { ProviderConnectService } from '../ai-gateway/connect';
|
|
|
6
6
|
import type { ProviderModelSelectionService } from '../ai-gateway/models/ProviderModelSelectionService';
|
|
7
7
|
import type { ProviderQuotaService } from '../ai-gateway/quota';
|
|
8
8
|
import { type ProviderCustomModelsService, type ProviderModelsService } from '../ai-gateway/models';
|
|
9
|
+
import type { PodInterfaceKeyGrant } from '../ai-gateway/pod/PodInterfaceKeyStore';
|
|
9
10
|
import type { AiConnectionsInvocationKeyIssuer } from '../ai-gateway/auth/AiConnectionsInvocationKeyIssuer';
|
|
10
11
|
import { type AiClientConfigurationCapabilityDescriptor } from '../service/AiClientConfigurationService';
|
|
11
12
|
export interface AiGatewayManagementHandlerOptions {
|
|
@@ -25,6 +26,11 @@ export interface AiGatewayManagementHandlerOptions {
|
|
|
25
26
|
gatewayAccessKeyRepository?: GatewayAccessKeyRepository;
|
|
26
27
|
/** Reuses the configured CSS authenticator; never trusts the claimed registration owner. */
|
|
27
28
|
validateClientCredential?: (apiKey: string) => Promise<AuthResult>;
|
|
29
|
+
/**
|
|
30
|
+
* Server-side Pod access for the owner. The wrapper being registered is the owner's own
|
|
31
|
+
* interface key, so registering it is also the moment Xpod is granted its use.
|
|
32
|
+
*/
|
|
33
|
+
podInterfaceKeys?: PodInterfaceKeyGrant;
|
|
28
34
|
aiClientConfiguration?: AiClientConfigurationCapabilityDescriptor;
|
|
29
35
|
aiConnectionInvocationKeyIssuer?: Pick<AiConnectionsInvocationKeyIssuer, 'issue' | 'issueClientConfiguration'>;
|
|
30
36
|
jsonBodyLimitBytes?: number;
|
|
@@ -5,8 +5,10 @@ const node_crypto_1 = require("node:crypto");
|
|
|
5
5
|
const global_logger_factory_1 = require("global-logger-factory");
|
|
6
6
|
const readBoundedJsonBody_1 = require("../http/readBoundedJsonBody");
|
|
7
7
|
const GatewayPrincipal_1 = require("../ai-gateway/auth/GatewayPrincipal");
|
|
8
|
+
const AuthContext_1 = require("../auth/AuthContext");
|
|
8
9
|
const models_1 = require("../ai-gateway/models");
|
|
9
10
|
const AiConnectionsServiceAccess_1 = require("../ai-gateway/service-access/AiConnectionsServiceAccess");
|
|
11
|
+
const OwnerPodAccess_1 = require("../ai-gateway/pod/OwnerPodAccess");
|
|
10
12
|
const AiClientConfigurationService_1 = require("../service/AiClientConfigurationService");
|
|
11
13
|
const errors_1 = require("../ai-gateway/errors");
|
|
12
14
|
const EmbeddingModelPolicy_1 = require("../../ai/service/EmbeddingModelPolicy");
|
|
@@ -117,6 +119,15 @@ function registerAiGatewayManagementRoutes(server, options) {
|
|
|
117
119
|
sendJson(response, 403, { error: 'CSS client credential belongs to another WebID' });
|
|
118
120
|
return;
|
|
119
121
|
}
|
|
122
|
+
if ((0, AuthContext_1.hasSolidClientCredentialsAuthority)(verified.context)) {
|
|
123
|
+
// Sealed before the record is written, because writing the record is the first thing
|
|
124
|
+
// that needs it: background components reach this Pod through the standard interface
|
|
125
|
+
// as the owner, never through the caller's network position.
|
|
126
|
+
await options.podInterfaceKeys?.saveKey(owner, {
|
|
127
|
+
clientId: verified.context.clientId,
|
|
128
|
+
clientSecret: verified.context.clientSecret,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
120
131
|
const name = normalizeOptionalString(body.name) ?? 'Xpod API Key';
|
|
121
132
|
const keyId = repository.createKeyId(owner, options.deployment);
|
|
122
133
|
const createdAt = new Date();
|
|
@@ -1329,7 +1340,7 @@ function sendCredentialPoolError(response, error) {
|
|
|
1329
1340
|
}
|
|
1330
1341
|
function sendGatewayAccessKeyError(response, error) {
|
|
1331
1342
|
const message = error instanceof Error ? error.message : String(error);
|
|
1332
|
-
if (
|
|
1343
|
+
if ((0, OwnerPodAccess_1.isPodAccessFailure)(message)) {
|
|
1333
1344
|
sendJson(response, 403, { error: 'service_access_missing' });
|
|
1334
1345
|
return;
|
|
1335
1346
|
}
|
|
@@ -1465,9 +1476,11 @@ function sendModelSelectionError(response, error) {
|
|
|
1465
1476
|
model_selection_exact_update_failed: { status: 409, error: 'model_selection_conflict' },
|
|
1466
1477
|
model_selection_provider_update_failed: { status: 409, error: 'model_selection_conflict' },
|
|
1467
1478
|
service_access_missing: { status: 403, error: 'service_access_missing' },
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1479
|
+
pod_interface_key_missing: { status: 403, error: 'service_access_missing' },
|
|
1480
|
+
pod_interface_key_rejected: { status: 403, error: 'service_access_missing' },
|
|
1481
|
+
caller_dpop_replay_unsupported: { status: 403, error: 'service_access_missing' },
|
|
1482
|
+
caller_owner_mismatch: { status: 403, error: 'pod_owner_mismatch' },
|
|
1483
|
+
caller_pod_access_unavailable: { status: 401, error: 'authentication_required' },
|
|
1471
1484
|
};
|
|
1472
1485
|
const mapped = stable[message];
|
|
1473
1486
|
if (mapped) {
|
|
@@ -164,6 +164,13 @@ export interface NetworkSettingsHandlerOptions {
|
|
|
164
164
|
logger?: Pick<ReturnType<typeof getLoggerFor>, 'warn' | 'error'>;
|
|
165
165
|
}
|
|
166
166
|
/** The ingress address this runtime listens on, when it published one. */
|
|
167
|
+
/**
|
|
168
|
+
* The address a provider console is configured with: the Gateway's tunnel entry.
|
|
169
|
+
*
|
|
170
|
+
* It is the listener that never treats a caller as local, so a tunnel reaching the runtime
|
|
171
|
+
* through it cannot inherit local trust whatever headers it carries. The banner the user
|
|
172
|
+
* copies is this one number; the Gateway's own port is what they type in a browser.
|
|
173
|
+
*/
|
|
167
174
|
export declare function readIngressAddress(env?: NodeJS.ProcessEnv): {
|
|
168
175
|
ingress?: {
|
|
169
176
|
port: number;
|
|
@@ -16,6 +16,13 @@ const readBoundedJsonBody_1 = require("../http/readBoundedJsonBody");
|
|
|
16
16
|
const AdminHandler_1 = require("./AdminHandler");
|
|
17
17
|
const TunnelProviderCatalog_1 = require("../../tunnel/TunnelProviderCatalog");
|
|
18
18
|
/** The ingress address this runtime listens on, when it published one. */
|
|
19
|
+
/**
|
|
20
|
+
* The address a provider console is configured with: the Gateway's tunnel entry.
|
|
21
|
+
*
|
|
22
|
+
* It is the listener that never treats a caller as local, so a tunnel reaching the runtime
|
|
23
|
+
* through it cannot inherit local trust whatever headers it carries. The banner the user
|
|
24
|
+
* copies is this one number; the Gateway's own port is what they type in a browser.
|
|
25
|
+
*/
|
|
19
26
|
function readIngressAddress(env = process.env) {
|
|
20
27
|
const port = Number.parseInt(env.XPOD_GATEWAY_INGRESS_PORT ?? '', 10);
|
|
21
28
|
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|