feat(engine): register MCP profiles and provider transitions
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env node
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import {
|
||||
compileL2ExecutionPlan,
|
||||
instantiateL2Connection,
|
||||
} from "../../packages/external-provider-contract/src/index.mjs";
|
||||
import {
|
||||
GELIOS_UNIT_CONTACTS_DATA_PRODUCT_ID,
|
||||
geliosProviderPackageV13,
|
||||
} from "../../packages/external-provider-contract/providers/gelios/v13/index.mjs";
|
||||
|
||||
const engineRoot = resolve(
|
||||
process.env.NODEDC_ENGINE_SOURCE_ROOT || "../NODEDC_ENGINE_INFRA",
|
||||
);
|
||||
const executionCatalogPath = resolve(
|
||||
engineRoot,
|
||||
"nodedc-source/server/assets/execution-plans/v1/catalog.json",
|
||||
);
|
||||
const securityCatalogPath = resolve(
|
||||
engineRoot,
|
||||
"nodedc-source/server/assets/provider-packages/v1/catalog.json",
|
||||
);
|
||||
|
||||
const executionCatalog = JSON.parse(
|
||||
await readFile(executionCatalogPath, "utf8"),
|
||||
);
|
||||
for (const value of ["1.5.0"]) {
|
||||
if (!executionCatalog.runtime.compilerVersions.includes(value)) {
|
||||
executionCatalog.runtime.compilerVersions.push(value);
|
||||
}
|
||||
}
|
||||
for (const value of ["bounded_response_lookup"]) {
|
||||
if (!executionCatalog.runtime.derivationKinds.includes(value)) {
|
||||
executionCatalog.runtime.derivationKinds.push(value);
|
||||
}
|
||||
}
|
||||
for (const value of ["response.lookup.first", "response.lookup.list"]) {
|
||||
if (!executionCatalog.runtime.ruleIds.includes(value)) {
|
||||
executionCatalog.runtime.ruleIds.push(value);
|
||||
}
|
||||
}
|
||||
executionCatalog.runtime.compilerVersions.sort();
|
||||
executionCatalog.runtime.derivationKinds.sort();
|
||||
executionCatalog.runtime.ruleIds.sort();
|
||||
executionCatalog.packages = executionCatalog.packages.filter(
|
||||
(providerPackage) => providerPackage.id !== geliosProviderPackageV13.id,
|
||||
);
|
||||
executionCatalog.packages.push(executionCatalogEntry());
|
||||
await writeFile(executionCatalogPath, `${JSON.stringify(executionCatalog, null, 2)}\n`);
|
||||
|
||||
const securityCatalog = JSON.parse(
|
||||
await readFile(securityCatalogPath, "utf8"),
|
||||
);
|
||||
securityCatalog.packages = securityCatalog.packages.filter(
|
||||
(providerPackage) => providerPackage.id !== geliosProviderPackageV13.id,
|
||||
);
|
||||
securityCatalog.packages.push(securityCatalogEntry());
|
||||
await writeFile(securityCatalogPath, `${JSON.stringify(securityCatalog, null, 2)}\n`);
|
||||
|
||||
console.log(JSON.stringify({
|
||||
ok: true,
|
||||
providerPackage: geliosProviderPackageV13.id,
|
||||
executionCatalogPath,
|
||||
securityCatalogPath,
|
||||
historicalExecutionPackagePreserved: executionCatalog.packages.some(
|
||||
(providerPackage) => providerPackage.id === "gelios.provider.v12",
|
||||
),
|
||||
historicalSecurityAuthorityPreserved: securityCatalog.packages.some(
|
||||
(providerPackage) => providerPackage.id === "gelios.provider.v12",
|
||||
),
|
||||
activeContactsAuthority: geliosProviderPackageV13.id,
|
||||
}, null, 2));
|
||||
|
||||
function selectedProfiles() {
|
||||
return geliosProviderPackageV13.collectionProfiles.filter(
|
||||
(profile) => profile.dataProductId === GELIOS_UNIT_CONTACTS_DATA_PRODUCT_ID,
|
||||
);
|
||||
}
|
||||
|
||||
function selectedCapabilityIds() {
|
||||
return new Set(selectedProfiles().flatMap((profile) => profile.capabilityIds));
|
||||
}
|
||||
|
||||
function executionCatalogEntry() {
|
||||
const profiles = selectedProfiles().map((profile) => {
|
||||
const connection = instantiateL2Connection(geliosProviderPackageV13, {
|
||||
tenantId: "tenant-catalog-build",
|
||||
connectionId: `catalog-${profile.id.replaceAll(".", "-")}`,
|
||||
collectionProfileId: profile.id,
|
||||
providerCredentialRef: "ndc-credref:catalog-build-provider-v13",
|
||||
});
|
||||
const plan = compileL2ExecutionPlan(geliosProviderPackageV13, connection);
|
||||
const { packageDigest: _packageDigest, ...artifacts } = plan.artifacts;
|
||||
return {
|
||||
id: profile.id,
|
||||
dataProductId: profile.dataProductId,
|
||||
capabilityIds: [...profile.capabilityIds],
|
||||
stepSignatures: plan.steps.map((step) => ({
|
||||
id: step.id,
|
||||
kind: step.kind,
|
||||
...(step.config.capabilityId ? { capabilityId: step.config.capabilityId } : {}),
|
||||
...(step.config.mappingContractId ? {
|
||||
mappingContractId: step.config.mappingContractId,
|
||||
} : {}),
|
||||
...(step.config.dataProductId ? { dataProductId: step.config.dataProductId } : {}),
|
||||
...(step.config.nodeType ? { nodeType: step.config.nodeType } : {}),
|
||||
})),
|
||||
artifacts,
|
||||
};
|
||||
});
|
||||
const capabilityIds = selectedCapabilityIds();
|
||||
return {
|
||||
id: geliosProviderPackageV13.id,
|
||||
providerId: geliosProviderPackageV13.providerId,
|
||||
version: geliosProviderPackageV13.version,
|
||||
contractDigest: canonicalDigest(geliosProviderPackageV13),
|
||||
providerCredential: {
|
||||
authModeId: "gelios.rest-rotating-bearer.v3",
|
||||
credentialType: "ndcProviderRotatingAccessApi",
|
||||
},
|
||||
publisher: {
|
||||
nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
|
||||
credentialType: "ndcDataProductWriterApi",
|
||||
},
|
||||
capabilities: geliosProviderPackageV13.capabilities
|
||||
.filter((capability) => capabilityIds.has(capability.id))
|
||||
.map((capability) => ({
|
||||
id: capability.id,
|
||||
contractDigest: canonicalDigest(capability),
|
||||
requestDigest: canonicalDigest(capability.request),
|
||||
method: capability.request.method,
|
||||
url: requestUrl(capability.request),
|
||||
})),
|
||||
profiles,
|
||||
};
|
||||
}
|
||||
|
||||
function securityCatalogEntry() {
|
||||
const productsByCapability = new Map(
|
||||
geliosProviderPackageV13.capabilities.map((capability) => [capability.id, new Set()]),
|
||||
);
|
||||
for (const profile of selectedProfiles()) {
|
||||
for (const capabilityId of profile.capabilityIds) {
|
||||
productsByCapability.get(capabilityId)?.add(profile.dataProductId);
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: geliosProviderPackageV13.id,
|
||||
version: geliosProviderPackageV13.version,
|
||||
providerId: geliosProviderPackageV13.providerId,
|
||||
providerCredential: {
|
||||
authModeId: "gelios.rest-rotating-bearer.v3",
|
||||
credentialType: "ndcProviderRotatingAccessApi",
|
||||
},
|
||||
capabilities: geliosProviderPackageV13.capabilities
|
||||
.filter((capability) => productsByCapability.get(capability.id)?.size)
|
||||
.map((capability) => ({
|
||||
id: capability.id,
|
||||
classification: capability.classification,
|
||||
status: capability.status,
|
||||
request: {
|
||||
method: capability.request.method,
|
||||
url: requestUrl(capability.request),
|
||||
},
|
||||
dataProductIds: [...productsByCapability.get(capability.id)].sort(),
|
||||
})),
|
||||
publisher: {
|
||||
nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
|
||||
credentialType: "ndcDataProductWriterApi",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function requestUrl(request) {
|
||||
const url = new URL(request.path, request.baseUrl);
|
||||
for (const [name, value] of Object.entries(request.query || {})) {
|
||||
url.searchParams.append(name, String(value));
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function canonicalDigest(value) {
|
||||
return `sha256:${createHash("sha256").update(JSON.stringify(stableValue(value)), "utf8").digest("hex")}`;
|
||||
}
|
||||
|
||||
function stableValue(value) {
|
||||
if (Array.isArray(value)) return value.map(stableValue);
|
||||
if (!value || typeof value !== "object") return value;
|
||||
return Object.fromEntries(
|
||||
Object.keys(value).sort().map((key) => [key, stableValue(value[key])]),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user