diff --git a/package-lock.json b/package-lock.json index 2d2d3b36..d641c2c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cheqd/studio", - "version": "3.16.0-develop.11", + "version": "3.16.0-develop.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@cheqd/studio", - "version": "3.16.0-develop.11", + "version": "3.16.0-develop.12", "license": "Apache-2.0", "dependencies": { "@cheqd/did-provider-cheqd": "^4.7.0-develop.1", @@ -33,6 +33,7 @@ "@verida/encryption-utils": "^3.0.1", "@verida/types": "^3.0.2", "@verida/vda-did-resolver": "^4.4.5", + "agntcy-dir": "^0.5.6", "bcrypt": "^5.1.1", "bs58": "^6.0.0", "cookie-parser": "^1.4.7", @@ -2264,6 +2265,28 @@ "pbts": "bin/pbts" } }, + "node_modules/@connectrpc/connect": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@connectrpc/connect/-/connect-2.1.1.tgz", + "integrity": "sha512-JzhkaTvM73m2K1URT6tv53k2RwngSmCXLZJgK580qNQOXRzZRR/BCMfZw3h+90JpnG6XksP5bYT+cz0rpUzUWQ==", + "license": "Apache-2.0", + "peerDependencies": { + "@bufbuild/protobuf": "^2.7.0" + } + }, + "node_modules/@connectrpc/connect-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@connectrpc/connect-node/-/connect-node-2.1.1.tgz", + "integrity": "sha512-s3TfsI1XF+n+1z6MBS9rTnFsxxR4Rw5wmdEnkQINli81ESGxcsfaEet8duzq8LVuuCupmhUsgpRo0Nv9pZkufg==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@bufbuild/protobuf": "^2.7.0", + "@connectrpc/connect": "2.1.1" + } + }, "node_modules/@cosmjs/amino": { "version": "0.36.2", "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.36.2.tgz", @@ -6428,6 +6451,69 @@ "license": "MIT", "optional": true }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@grpc/proto-loader/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -7208,6 +7294,16 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/@jsdevtools/ono": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", @@ -15032,13 +15128,146 @@ "@octokit/openapi-types": "^25.0.0" } }, + "node_modules/@peculiar/asn1-cms": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.0.tgz", + "integrity": "sha512-2uZqP+ggSncESeUF/9Su8rWqGclEfEiz1SyU02WX5fUONFfkjzS2Z/F1Li0ofSmf4JqYXIOdCAZqIXAIBAT1OA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "@peculiar/asn1-x509-attr": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-cms/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-csr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.0.tgz", + "integrity": "sha512-BeWIu5VpTIhfRysfEp73SGbwjjoLL/JWXhJ/9mo4vXnz3tRGm+NGm3KNcRzQ9VMVqwYS2RHlolz21svzRXIHPQ==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-csr/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-ecc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.0.tgz", + "integrity": "sha512-FF3LMGq6SfAOwUG2sKpPXblibn6XnEIKa+SryvUl5Pik+WR9rmRA3OCiwz8R3lVXnYnyRkSZsSLdml8H3UiOcw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-ecc/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-pfx": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.0.tgz", + "integrity": "sha512-rtUvtf+tyKGgokHHmZzeUojRZJYPxoD/jaN1+VAB4kKR7tXrnDCA/RAWXAIhMJJC+7W27IIRGe9djvxKgsldCQ==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-pkcs8": "^2.6.0", + "@peculiar/asn1-rsa": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pfx/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-pkcs8": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.0.tgz", + "integrity": "sha512-KyQ4D8G/NrS7Fw3XCJrngxmjwO/3htnA0lL9gDICvEQ+GJ+EPFqldcJQTwPIdvx98Tua+WjkdKHSC0/Km7T+lA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs8/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-pkcs9": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.0.tgz", + "integrity": "sha512-b78OQ6OciW0aqZxdzliXGYHASeCvvw5caqidbpQRYW2mBtXIX2WhofNXTEe7NyxTb0P6J62kAAWLwn0HuMF1Fw==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.6.0", + "@peculiar/asn1-pfx": "^2.6.0", + "@peculiar/asn1-pkcs8": "^2.6.0", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "@peculiar/asn1-x509-attr": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-pkcs9/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-rsa": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.0.tgz", + "integrity": "sha512-Nu4C19tsrTsCp9fDrH+sdcOKoVfdfoQQ7S3VqjJU6vedR7tY3RLkQ5oguOIB3zFW33USDUuYZnPEQYySlgha4w==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-rsa/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@peculiar/asn1-schema": { - "version": "2.3.15", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.15.tgz", - "integrity": "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz", + "integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==", "license": "MIT", "dependencies": { - "asn1js": "^3.0.5", + "asn1js": "^3.0.6", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } @@ -15049,6 +15278,42 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/@peculiar/asn1-x509": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.0.tgz", + "integrity": "sha512-uzYbPEpoQiBoTq0/+jZtpM6Gq6zADBx+JNFP3yqRgziWBxQ/Dt/HcuvRfm9zJTPdRcBqPNdaRHTVwpyiq6iNMA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "asn1js": "^3.0.6", + "pvtsutils": "^1.3.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.0.tgz", + "integrity": "sha512-MuIAXFX3/dc8gmoZBkwJWxUWOSvG4MMDntXhrOZpJVMkYX+MYc/rUAU2uJOved9iJEoiUx7//3D8oG83a78UJA==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.0", + "asn1js": "^3.0.6", + "tslib": "^2.8.1" + } + }, + "node_modules/@peculiar/asn1-x509-attr/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@peculiar/asn1-x509/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@peculiar/json-schema": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", @@ -15083,6 +15348,31 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/@peculiar/x509": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.0.tgz", + "integrity": "sha512-Yc4PDxN3OrxUPiXgU63c+ZRXKGE8YKF2McTciYhUHFtHVB0KMnjeFSU0qpztGhsp4P0uKix4+J2xEpIEDu8oXg==", + "license": "MIT", + "dependencies": { + "@peculiar/asn1-cms": "^2.5.0", + "@peculiar/asn1-csr": "^2.5.0", + "@peculiar/asn1-ecc": "^2.5.0", + "@peculiar/asn1-pkcs9": "^2.5.0", + "@peculiar/asn1-rsa": "^2.5.0", + "@peculiar/asn1-schema": "^2.5.0", + "@peculiar/asn1-x509": "^2.5.0", + "pvtsutils": "^1.3.6", + "reflect-metadata": "^0.2.2", + "tslib": "^2.8.1", + "tsyringe": "^4.10.0" + } + }, + "node_modules/@peculiar/x509/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -15154,6 +15444,34 @@ "node": ">=12" } }, + "node_modules/@protobuf-ts/grpc-transport": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/grpc-transport/-/grpc-transport-2.11.1.tgz", + "integrity": "sha512-l6wrcFffY+tuNnuyrNCkRM8hDIsAZVLA8Mn7PKdVyYxITosYh60qW663p9kL6TWXYuDCL3oxH8ih3vLKTDyhtg==", + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1", + "@protobuf-ts/runtime-rpc": "^2.11.1" + }, + "peerDependencies": { + "@grpc/grpc-js": "^1.6.0" + } + }, + "node_modules/@protobuf-ts/runtime": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.11.1.tgz", + "integrity": "sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@protobuf-ts/runtime-rpc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.11.1.tgz", + "integrity": "sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ==", + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/runtime": "^2.11.1" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -15638,6 +15956,19 @@ } } }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", + "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@scarf/scarf": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", @@ -23157,6 +23488,25 @@ "node": ">=8" } }, + "node_modules/agntcy-dir": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/agntcy-dir/-/agntcy-dir-0.5.6.tgz", + "integrity": "sha512-TPZP1ObFkc6XHTvLu87eoDPQpeW7FJiNydHQRLCmPcm6hTcTbf79HMvmvvf5rS20oO0eb7AwpXciAzvHm+9X9A==", + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "^2.9.0", + "@connectrpc/connect": "^2.1.0", + "@connectrpc/connect-node": "^2.1.0", + "@grpc/grpc-js": "^1.13.4", + "spiffe": "^0.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "4.50.2" + } + }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -32180,6 +32530,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, "node_modules/lodash.capitalize": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", @@ -42908,6 +43264,21 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/spiffe": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/spiffe/-/spiffe-0.4.0.tgz", + "integrity": "sha512-yjHNB+r2h/ybIIjdwl7TPRHNPfi95oNflRDfUo70w9nV7hrSluPfFecKHDx3X1j3rkO4iuwbssTrwwyJIjpWPw==", + "license": "MIT", + "dependencies": { + "@grpc/grpc-js": "^1.9.11", + "@peculiar/webcrypto": "^1.4.3", + "@peculiar/x509": "^1.9.5", + "@protobuf-ts/grpc-transport": "^2.9.1", + "@protobuf-ts/runtime": "^2.9.1", + "@protobuf-ts/runtime-rpc": "^2.9.1", + "protobufjs": "^7.2.5" + } + }, "node_modules/split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -44382,6 +44753,24 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/tsyringe": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", + "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "license": "MIT", + "dependencies": { + "tslib": "^1.9.3" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tsyringe/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", diff --git a/package.json b/package.json index 8b69ed69..e13598d0 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@verida/encryption-utils": "^3.0.1", "@verida/types": "^3.0.2", "@verida/vda-did-resolver": "^4.4.5", + "agntcy-dir": "^0.5.6", "bcrypt": "^5.1.1", "bs58": "^6.0.0", "cookie-parser": "^1.4.7", diff --git a/src/app.ts b/src/app.ts index 9b79afeb..c3e8b8b1 100644 --- a/src/app.ts +++ b/src/app.ts @@ -33,6 +33,7 @@ import { OrganisationController } from './controllers/admin/organisation.js'; import { AccreditationController } from './controllers/api/accreditation.js'; import { OperationController } from './controllers/api/operation.js'; import { ProvidersController } from './controllers/api/providers.controller.js'; +import { OasfController } from './controllers/api/oasf.js'; dotenv.config(); @@ -405,6 +406,10 @@ class App { app.get('/admin/organisation/get', new OrganisationController().get); } + app.post('/oasf/publish', OasfController.recordPublishValidator, new OasfController().publishRecord); + app.get('/oasf/search', OasfController.recordSearchValidator, new OasfController().searchRecord); + app.get('/oasf/:cid', OasfController.recordGetValidator, new OasfController().getRecord); + // 404 for all other requests app.all('*', (_req, res) => res.status(StatusCodes.BAD_REQUEST).send('Bad request')); } diff --git a/src/controllers/api/oasf.ts b/src/controllers/api/oasf.ts new file mode 100644 index 00000000..3d6803fc --- /dev/null +++ b/src/controllers/api/oasf.ts @@ -0,0 +1,389 @@ +import type { Request, Response } from 'express'; +import { StatusCodes } from 'http-status-codes'; +import { check, query } from 'express-validator'; +import { validate } from '../validator/decorator.js'; +import type { + PublishRecordRequestBody, + PublishRecordResponseBody, + UnsuccessfulPublishRecordResponseBody, + SearchRecordQuery, + SearchRecordResponseBody, + UnsuccessfulSearchRecordResponseBody, + GetRecordParams, + GetRecordQuery, + GetRecordResponseBody, + UnsuccessfulGetRecordResponseBody, +} from '../../types/oasf.js'; +import { OasfService } from '../../services/api/oasf.js'; + +export class OasfController { + // Validators + public static recordPublishValidator = [ + check('data').exists().withMessage('data field is required').bail(), + check('data.name') + .exists() + .withMessage('name is required') + .isString() + .withMessage('name must be a string') + .bail(), + check('data.version') + .exists() + .withMessage('version is required') + .matches(/^\d+\.\d+\.\d+$/) + .withMessage('version must follow semantic versioning (e.g., 1.0.0)') + .bail(), + check('data.schema_version') + .exists() + .withMessage('schema_version is required') + .isString() + .withMessage('schema_version must be a string') + .bail(), + check('data.description').optional().isString().withMessage('description must be a string').bail(), + check('data.authors').optional().isArray().withMessage('authors must be an array').bail(), + check('data.type') + .optional() + .isString() + .isIn(['agent', 'organization', 'service', 'mcp-server']) + .withMessage('Invalid record type') + .bail(), + check('data.skills').optional().isArray().withMessage('skills must be an array').bail(), + check('data.locators').optional().isArray().withMessage('locators must be an array').bail(), + check('data.domains').optional().isArray().withMessage('domains must be an array').bail(), + check('data.modules').optional().isArray().withMessage('modules must be an array').bail(), + ]; + + public static recordSearchValidator = [ + query('name').optional().isString().withMessage('name must be a string').bail(), + query('version').optional().isString().withMessage('version must be a string').bail(), + query('skill').optional().isString().withMessage('skill must be a string').bail(), + query('skill_id').optional().isInt().withMessage('skill_id must be an integer').bail(), + query('domain').optional().isString().withMessage('domain must be a string').bail(), + query('locator').optional().isString().withMessage('locator must be a string').bail(), + query('module').optional().isString().withMessage('module must be a string').bail(), + query('type') + .optional() + .isString() + .isIn(['agent', 'organization', 'service', 'mcp-server']) + .withMessage('Invalid record type') + .bail(), + query('page').optional().isInt({ min: 1 }).withMessage('page must be a positive integer').bail(), + query('limit').optional().isInt({ min: 1, max: 100 }).withMessage('limit must be between 1 and 100').bail(), + ]; + + public static recordGetValidator = [ + check('cid') + .exists() + .withMessage('cid was not provided') + .isString() + .withMessage('cid must be a string') + .notEmpty() + .withMessage('cid cannot be empty') + .bail(), + query('verify').optional().isBoolean().withMessage('verify must be a boolean').bail(), + ]; + + /** + * @openapi + * + * /oasf/publish: + * post: + * tags: [ Records ] + * summary: Publish an OASF record to the directory. + * description: This endpoint publishes an OASF-compliant record to the Agent Directory. The record can represent an agent, organization, service, or MCP server. + * requestBody: + * content: + * application/x-www-form-urlencoded: + * schema: + * $ref: '#/components/schemas/PublishRecordRequest' + * application/json: + * schema: + * $ref: '#/components/schemas/PublishRecordRequest' + * responses: + * 201: + * description: The record was successfully published. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/PublishRecordResult' + * 400: + * description: A problem with the input fields has occurred. Additional state information plus metadata may be available in the response body. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/InvalidRequest' + * example: + * error: InvalidRequest + * 401: + * $ref: '#/components/schemas/UnauthorizedError' + * 500: + * description: An internal error has occurred. Additional state information plus metadata may be available in the response body. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/InvalidRequest' + * example: + * error: Internal Error + */ + @validate + public async publishRecord(request: Request, response: Response) { + const record = request.body as PublishRecordRequestBody; + + // Get directory service + const oasfService = new OasfService(); + + try { + // Validate against OASF schema (optional) + const isValid = await oasfService.validateOASFSchema(record); + if (!isValid) { + return response.status(StatusCodes.BAD_REQUEST).json({ + error: 'Record does not conform to OASF schema', + } satisfies UnsuccessfulPublishRecordResponseBody); + } + + // Publish to directory + const result = await oasfService.publish(response.locals.customer, record); + + // Return the response + return response.status(StatusCodes.CREATED).json({ + success: true, + cid: result.cid, + message: 'Record published successfully', + data: { + name: record.data.name, + version: record.data.version, + cid: result.cid, + published_at: new Date().toISOString(), + }, + } satisfies PublishRecordResponseBody); + } catch (error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + error: `Internal error: ${(error as Error)?.message || error}`, + } satisfies UnsuccessfulPublishRecordResponseBody); + } + } + + /** + * @openapi + * + * /oasf/search: + * get: + * tags: [ Records ] + * summary: Search for records in the directory. + * description: This endpoint searches for OASF records in the Agent Directory based on various criteria such as name, version, skills, domains, and more. + * parameters: + * - name: name + * description: Name of the record to search for. + * in: query + * schema: + * type: string + * - name: version + * description: Version of the record to search for. + * in: query + * schema: + * type: string + * - name: skill + * description: Skill name to filter by. + * in: query + * schema: + * type: string + * - name: skill_id + * description: Skill ID to filter by. + * in: query + * schema: + * type: integer + * - name: domain + * description: Domain to filter by. + * in: query + * schema: + * type: string + * - name: locator + * description: Locator type to filter by. + * in: query + * schema: + * type: string + * - name: module + * description: Module name to filter by. + * in: query + * schema: + * type: string + * - name: type + * description: Type of record to filter by. + * in: query + * schema: + * type: string + * enum: + * - agent + * - organization + * - service + * - mcp-server + * - name: page + * description: Page number for pagination (default 1). + * in: query + * schema: + * type: integer + * minimum: 1 + * - name: limit + * description: Number of results per page (default 20, max 100). + * in: query + * schema: + * type: integer + * minimum: 1 + * maximum: 100 + * responses: + * 200: + * description: The request was successful. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SearchRecordResult' + * 400: + * description: A problem with the input fields has occurred. Additional state information plus metadata may be available in the response body. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/InvalidRequest' + * example: + * error: InvalidRequest + * 401: + * $ref: '#/components/schemas/UnauthorizedError' + * 500: + * description: An internal error has occurred. Additional state information plus metadata may be available in the response body. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/InvalidRequest' + * example: + * error: Internal Error + */ + @validate + public async searchRecord(request: Request, response: Response) { + const query = request.query as SearchRecordQuery; + + // Get directory service + const oasfService = new OasfService(); + + try { + // Set defaults for pagination + const { page = 1, limit = 20 } = request.query as SearchRecordQuery; + + // Search directory + const results = await oasfService.search({ + ...query, + page, + limit, + }); + + // Return paginated results + return response.status(StatusCodes.OK).json({ + success: true, + data: results.records, + pagination: { + page, + limit, + total: results.total, + total_pages: Math.ceil(results.total / limit), + }, + } satisfies SearchRecordResponseBody); + } catch (error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + error: `Internal error: ${(error as Error)?.message || error}`, + } satisfies UnsuccessfulSearchRecordResponseBody); + } + } + + /** + * @openapi + * + * /oasf/{cid}: + * get: + * tags: [ Records ] + * summary: Fetch a record by CID. + * description: This endpoint fetches a specific OASF record from the Agent Directory using its Content Identifier (CID). Optionally, the record's signature can be verified. + * parameters: + * - name: cid + * description: Content Identifier (CID) of the record to fetch. + * in: path + * schema: + * type: string + * required: true + * - name: verify + * description: Whether to verify the record's signature. + * in: query + * schema: + * type: boolean + * responses: + * 200: + * description: The request was successful. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/GetRecordResult' + * 400: + * description: A problem with the input fields has occurred. Additional state information plus metadata may be available in the response body. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/InvalidRequest' + * example: + * error: InvalidRequest + * 401: + * $ref: '#/components/schemas/UnauthorizedError' + * 404: + * description: The record was not found. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/InvalidRequest' + * example: + * error: Record not found + * 500: + * description: An internal error has occurred. Additional state information plus metadata may be available in the response body. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/InvalidRequest' + * example: + * error: Internal Error + */ + @validate + public async getRecord(request: Request, response: Response) { + const { cid } = request.params as unknown as GetRecordParams; + const { verify } = request.query as GetRecordQuery; + + // Get directory service + const oasfService = new OasfService(); + + try { + // Fetch record from directory + const record = await oasfService.getRecord(cid); + + if (!record) { + return response.status(StatusCodes.NOT_FOUND).json({ + error: `Record with CID: ${cid} not found`, + } satisfies UnsuccessfulGetRecordResponseBody); + } + + // Optional: Verify signature + let verificationResult = null; + if (verify === 'true' || verify === true) { + verificationResult = await oasfService.verifyRecord(cid); + } + + // Return record with metadata + return response.status(StatusCodes.OK).json({ + success: true, + data: record, + metadata: { + cid, + retrieved_at: new Date().toISOString(), + verified: verificationResult?.verified || null, + signature: verificationResult || null, + }, + } satisfies GetRecordResponseBody); + } catch (error) { + return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ + error: `Internal error: ${(error as Error)?.message || error}`, + } satisfies UnsuccessfulGetRecordResponseBody); + } + } +} diff --git a/src/middleware/auth/routes/api/oasf-auth.ts b/src/middleware/auth/routes/api/oasf-auth.ts new file mode 100644 index 00000000..aacc8068 --- /dev/null +++ b/src/middleware/auth/routes/api/oasf-auth.ts @@ -0,0 +1,10 @@ +import { AuthRuleProvider } from '../auth-rule-provider.js'; + +export class AgntcyAuthProvider extends AuthRuleProvider { + constructor() { + super(); + this.registerRule('/oasf/search', 'GET', 'read:oasf', { skipNamespace: true, allowUnauthorized: true }); + this.registerRule('/oasf/(.*)', 'GET', 'read:oasf', { skipNamespace: true, allowUnauthorized: true }); + this.registerRule('/oasf/publish', 'POST', 'create:oasf', { skipNamespace: true }); + } +} diff --git a/src/middleware/authentication.ts b/src/middleware/authentication.ts index 6dfe4759..41e228af 100644 --- a/src/middleware/authentication.ts +++ b/src/middleware/authentication.ts @@ -22,6 +22,7 @@ import type { UnsuccessfulResponseBody } from '../types/shared.js'; import { AccreditationAuthRuleProvider } from './auth/routes/api/accreditation-auth.js'; import { EventAuthRuleProvider } from './auth/routes/api/event-auth.js'; import { ProvidersAuthRuleProvider } from './auth/routes/api/provider-auth.js'; +import { AgntcyAuthProvider } from './auth/routes/api/oasf-auth.js'; dotenv.config(); @@ -52,6 +53,7 @@ export class Authentication { authRuleRepository.push(new AdminAuthRuleProvider()); authRuleRepository.push(new ProvidersAuthRuleProvider()); + authRuleRepository.push(new AgntcyAuthProvider()); this.apiGuardian = new APIGuard(authRuleRepository, this.oauthProvider); // Initial auth handler diff --git a/src/services/api/oasf.ts b/src/services/api/oasf.ts new file mode 100644 index 00000000..8292313b --- /dev/null +++ b/src/services/api/oasf.ts @@ -0,0 +1,497 @@ +import { Client, Config, models } from 'agntcy-dir'; +import { create } from '@bufbuild/protobuf'; +import { CustomerEntity } from '../../database/entities/customer.entity.js'; +import { + queryTypeMap, + type PublishRecordRequestBody, + type SearchRecordQuery, + type VerificationResult, +} from '../../types/oasf.js'; + +export class OasfService { + private client: Client; + private oasfSchemaUrl: string; + + constructor() { + const serverAddress = process.env.DIRECTORY_SERVER_URL || 'localhost:8888'; + + this.oasfSchemaUrl = process.env.OASF_SCHEMA_SERVER_URL || 'https://schema.oasf.outshift.com'; + + this.client = new Client(new Config(serverAddress)); + } + + /** + * Validate OASF record against schema server + */ + async validateOASFSchema(record: any): Promise { + try { + if (process.env.SKIP_OASF_VALIDATION === 'true') { + return true; + } + + const response = await fetch(`${this.oasfSchemaUrl}/api/validate`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + schema_version: record.data?.schema_version || '0.7.0', + record: record, + }), + signal: AbortSignal.timeout(10000), + }); + + const result = await response.json(); + + if (response.ok && result.valid === true) { + return true; + } + + if (result.errors) { + console.error('OASF validation errors:', result.errors); + } + + return false; + } catch (error) { + console.error('Error validating OASF schema:', error); + console.warn('OASF schema server unreachable, skipping validation'); + return true; + } + } + + /** + * Publish record to directory + */ + async publish(customer: CustomerEntity, record: PublishRecordRequestBody): Promise<{ cid: string }> { + try { + // Convert to OASF format (match example structure exactly) + const oasfRecord = this.toOASFRecord(record); + + // Remove undefined/null fields to avoid protobuf errors + const cleanRecord = JSON.parse( + JSON.stringify(oasfRecord, (key, value) => { + // Remove undefined and null values + if (value === undefined || value === null) { + return undefined; + } + // Remove empty strings for optional fields + if (value === '' && key !== 'description') { + return undefined; + } + return value; + }) + ); + + console.log('Publishing record:', JSON.stringify(cleanRecord, null, 2)); + + // Push records + const pushed_refs = await this.client.push([cleanRecord]); + + if (!pushed_refs || pushed_refs.length === 0) { + throw new Error('No CID returned from directory server'); + } + + const cid = pushed_refs[0].cid; + + // Auto-publish to DHT for discovery + try { + const recordRefs = create(models.routing_v1.RecordRefsSchema, { + refs: pushed_refs, + }); + + const publishRequest = create(models.routing_v1.PublishRequestSchema, { + request: { + case: 'recordRefs', + value: recordRefs, + }, + }); + + await this.client.publish(publishRequest); + } catch (err) { + console.warn('Failed to publish to DHT:', err); + } + + return { cid }; + } catch (error) { + console.error('Error publishing record:', error); + throw new Error(`Failed to publish record: ${(error as Error).message}`); + } + } + + /** + * Search for records in directory + */ + async search(query: SearchRecordQuery): Promise<{ records: PublishRecordRequestBody[]; total: number }> { + try { + // Build search queries array + const queries: any[] = []; + + for (const [field, queryType] of Object.entries(queryTypeMap)) { + const value = query[field as keyof SearchRecordQuery]; + if (value !== undefined) { + queries.push({ type: queryType, value: String(value) }); + } + } + + // Create search request using protobuf + const searchRequest = create(models.search_v1.SearchRecordsRequestSchema, { + queries: queries.length > 0 ? queries : [], + limit: query.limit || 20, + offset: query.page ? (query.page - 1) * (query.limit || 20) : 0, + }); + + console.log('Search request:', JSON.stringify(searchRequest, null, 2)); + + // Search using SDK + const results = await this.client.searchRecords(searchRequest); + + console.log('Search results:', results); + + if (!results || results.length === 0) { + return { records: [], total: 0 }; + } + + // Convert records to response format + const records = results + .map((response: any) => { + try { + // SearchRecordsResponse contains an optional 'record' field + if (!response || !response.record) { + console.warn('Response missing record field:', response); + return null; + } + return this.fromOASFRecord(response.record); + } catch (e) { + console.error('Error parsing record:', e); + return null; + } + }) + .filter(Boolean) as PublishRecordRequestBody[]; + + return { + records, + total: records.length, + }; + } catch (error) { + console.error('Error searching records:', error); + throw new Error(`Failed to search records: ${(error as Error).message}`); + } + } + + /** + * Get specific record by CID + */ + async getRecord(cid: string): Promise { + try { + // Create RecordRef using protobuf constructor + const recordRef = this.createRecordRef(cid); + + // Pull record using SDK + const results = await this.client.pull([recordRef]); + + if (!results || results.length === 0) { + return null; + } + + return this.fromOASFRecord(results[0]); + } catch (error) { + const errorMsg = (error as Error).message.toLowerCase(); + if (errorMsg.includes('not found') || errorMsg.includes('not_found')) { + return null; + } + console.error('Error getting record:', error); + throw new Error(`Failed to get record: ${(error as Error).message}`); + } + } + + /** + * Verify record signature + */ + async verifyRecord(cid: string): Promise { + try { + // Note: The SDK might have a different verify API + // For now, return unverified since we don't have signature in the example + console.warn('Verify not implemented in example - returning unverified'); + + return { + verified: false, + error: 'Verification not implemented', + }; + } catch (error) { + console.error('Error verifying record:', error); + return { + verified: false, + error: (error as Error).message, + }; + } + } + + /** + * Search across distributed network + */ + async searchNetwork( + customer: CustomerEntity, + query: SearchRecordQuery + ): Promise<{ records: any[]; total: number }> { + try { + // Build routing queries + const queries: any[] = []; + + if (query.skill) { + queries.push({ + type: models.routing_v1.RecordQueryType.SKILL, + value: query.skill, + }); + } + + if (query.domain) { + queries.push({ + type: models.routing_v1.RecordQueryType.DOMAIN, + value: query.domain, + }); + } + + // Create ListRequest using protobuf constructor + const listRequest = create(models.routing_v1.ListRequestSchema, { + queries: queries.length > 0 ? queries : [], + limit: query.limit || 20, + }); + + // List published records + const results = await this.client.list(listRequest); + + return { + records: results || [], + total: results?.length || 0, + }; + } catch (error) { + console.error('Error searching network:', error); + throw new Error(`Failed to search network: ${(error as Error).message}`); + } + } + + /** + * Get OASF skills taxonomy + */ + async getSkillsTaxonomy(): Promise { + try { + const response = await fetch(`${this.oasfSchemaUrl}/api/skills`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + signal: AbortSignal.timeout(10000), + }); + + if (!response.ok) { + throw new Error(`Failed to fetch skills: ${response.statusText}`); + } + + const data = await response.json(); + return data.skills || data || []; + } catch (error) { + console.error('Error fetching skills taxonomy:', error); + return []; + } + } + + /** + * Get OASF domains taxonomy + */ + async getDomainsTaxonomy(): Promise { + try { + const response = await fetch(`${this.oasfSchemaUrl}/api/domains`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + signal: AbortSignal.timeout(10000), + }); + + if (!response.ok) { + throw new Error(`Failed to fetch domains: ${response.statusText}`); + } + + const data = await response.json(); + return data.domains || data || []; + } catch (error) { + console.error('Error fetching domains taxonomy:', error); + return []; + } + } + + /** + * Get OASF modules + */ + async getModules(): Promise { + try { + const response = await fetch(`${this.oasfSchemaUrl}/api/modules`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + signal: AbortSignal.timeout(10000), + }); + + if (!response.ok) { + throw new Error(`Failed to fetch modules: ${response.statusText}`); + } + + const data = await response.json(); + return data.modules || data || []; + } catch (error) { + console.error('Error fetching modules:', error); + return []; + } + } + + /** + * Get OASF schema versions + */ + async getSchemaVersions(): Promise { + try { + const response = await fetch(`${this.oasfSchemaUrl}/api/versions`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + signal: AbortSignal.timeout(10000), + }); + + if (!response.ok) { + throw new Error(`Failed to fetch versions: ${response.statusText}`); + } + + const data = await response.json(); + return data.versions || data || []; + } catch (error) { + console.error('Error fetching schema versions:', error); + return ['0.7.0']; + } + } + + /** + * Delete a record by CID (unpublishes from DHT) + */ + async deleteRecord(customer: CustomerEntity, cid: string): Promise<{ deleted: boolean }> { + try { + // Create RecordRef using protobuf constructor + const recordRef = this.createRecordRef(cid); + + // Create RecordRefs using protobuf constructor + const recordRefs = create(models.routing_v1.RecordRefsSchema, { + refs: [recordRef], + }); + + // Create UnpublishRequest using protobuf constructor + const unpublishRequest = create(models.routing_v1.UnpublishRequestSchema, { + request: { + case: 'recordRefs', + value: recordRefs, + }, + }); + + // Unpublish from DHT + await this.client.unpublish(unpublishRequest); + + // Delete from storage + await this.client.delete([recordRef]); + + return { deleted: true }; + } catch (error) { + console.error('Error deleting record:', error); + throw new Error(`Failed to delete record: ${(error as Error).message}`); + } + } + + /** + * Update a record (creates new version with new CID) + */ + async updateRecord( + customer: CustomerEntity, + oldCid: string, + record: PublishRecordRequestBody + ): Promise<{ cid: string }> { + try { + // Delete old version + await this.deleteRecord(customer, oldCid); + + // Publish new version + return await this.publish(customer, record); + } catch (error) { + console.error('Error updating record:', error); + throw new Error(`Failed to update record: ${(error as Error).message}`); + } + } + + /** + * Get info about a record (metadata without pulling full content) + */ + async getRecordInfo(customer: CustomerEntity, cid: string): Promise { + try { + // Create RecordRef using protobuf constructor + const recordRef = this.createRecordRef(cid); + + // Lookup metadata + const results = await this.client.lookup([recordRef]); + + if (!results || results.length === 0) { + throw new Error('Record not found'); + } + + return results[0]; + } catch (error) { + console.error('Error getting record info:', error); + throw new Error(`Failed to get record info: ${(error as Error).message}`); + } + } + + /** + * Convert request body to OASF record format + * Must match the exact structure from the official example + */ + private toOASFRecord(body: PublishRecordRequestBody): any { + const { data } = body; + + // Return the exact structure from the example - just the data wrapper + return { + data: { + name: data.name, + version: data.version, + schema_version: data.schema_version || '0.7.0', + description: data.description || '', + authors: data.authors || [], + created_at: data.created_at || new Date().toISOString(), + uid: data.uid, // Include identity if provided + type: data.type, + skills: data.skills || [], + locators: data.locators || [], + domains: data.domains || [], + modules: data.modules || [], + }, + }; + } + + /** + * Convert OASF record back to response format + */ + private fromOASFRecord(record: any): PublishRecordRequestBody { + const data = record.data || record; + + return { + data: { + name: data.name, + version: data.version, + schema_version: data.schema_version, + uid: data.uid, + description: data.description, + authors: data.authors, + created_at: data.created_at, + type: data.type, + skills: data.skills || [], + locators: data.locators || [], + domains: data.domains || [], + modules: data.modules || [], + }, + }; + } + + /** + * Create a RecordRef protobuf message + */ + private createRecordRef(cid: string) { + return create(models.core_v1.RecordRefSchema, { + cid: cid, + }); + } +} diff --git a/src/static/swagger-api.json b/src/static/swagger-api.json index 4732eeff..b7273461 100644 --- a/src/static/swagger-api.json +++ b/src/static/swagger-api.json @@ -3247,6 +3247,144 @@ } } } + }, + "PublishRecordRequest": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "name", + "version", + "schema_version" + ], + "properties": { + "name": { + "type": "string", + "description": "Name of the record" + }, + "version": { + "type": "string", + "description": "Version of the record" + }, + "schema_version": { + "type": "string", + "description": "Schema version" + }, + "description": { + "type": "string", + "description": "Optional description" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Optional list of authors" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "Optional creation timestamp" + }, + "type": { + "type": "string", + "enum": [ + "agent", + "organization", + "service", + "mcp-server" + ], + "description": "Optional record type" + }, + "skills": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "number" + }, + "locators": { + "type": "array" + }, + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "domains": { + "type": "array", + "items": { + "type": "object" + }, + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "number" + }, + "modules": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "PublishRecordResult": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "example": true + }, + "cid": { + "type": "string", + "description": "Content identifier of the published record" + }, + "message": { + "type": "string" + }, + "data": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "cid": { + "type": "string" + }, + "published_at": { + "type": "string", + "format": "date-time" + } + } + } + } } } }, @@ -5964,6 +6102,293 @@ } } }, + "/oasf/publish": { + "post": { + "tags": [ + "Records" + ], + "summary": "Publish an OASF record to the directory.", + "description": "This endpoint publishes an OASF-compliant record to the Agent Directory. The record can represent an agent, organization, service, or MCP server.", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublishRecordRequest" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublishRecordRequest" + } + } + } + }, + "responses": { + "201": { + "description": "The record was successfully published.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublishRecordResult" + } + } + } + }, + "400": { + "description": "A problem with the input fields has occurred. Additional state information plus metadata may be available in the response body.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequest" + }, + "example": { + "error": "InvalidRequest" + } + } + } + }, + "401": { + "$ref": "#/components/schemas/UnauthorizedError" + }, + "500": { + "description": "An internal error has occurred. Additional state information plus metadata may be available in the response body.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequest" + }, + "example": { + "error": "Internal Error" + } + } + } + } + } + } + }, + "/oasf/search": { + "get": { + "tags": [ + "Records" + ], + "summary": "Search for records in the directory.", + "description": "This endpoint searches for OASF records in the Agent Directory based on various criteria such as name, version, skills, domains, and more.", + "parameters": [ + { + "name": "name", + "description": "Name of the record to search for.", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "version", + "description": "Version of the record to search for.", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skill", + "description": "Skill name to filter by.", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "skill_id", + "description": "Skill ID to filter by.", + "in": "query", + "schema": { + "type": "integer" + } + }, + { + "name": "domain", + "description": "Domain to filter by.", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "locator", + "description": "Locator type to filter by.", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "module", + "description": "Module name to filter by.", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "type", + "description": "Type of record to filter by.", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "agent", + "organization", + "service", + "mcp-server" + ] + } + }, + { + "name": "page", + "description": "Page number for pagination (default 1).", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "limit", + "description": "Number of results per page (default 20, max 100).", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "The request was successful.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchRecordResult" + } + } + } + }, + "400": { + "description": "A problem with the input fields has occurred. Additional state information plus metadata may be available in the response body.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequest" + }, + "example": { + "error": "InvalidRequest" + } + } + } + }, + "401": { + "$ref": "#/components/schemas/UnauthorizedError" + }, + "500": { + "description": "An internal error has occurred. Additional state information plus metadata may be available in the response body.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequest" + }, + "example": { + "error": "Internal Error" + } + } + } + } + } + } + }, + "/oasf/{cid}": { + "get": { + "tags": [ + "Records" + ], + "summary": "Fetch a record by CID.", + "description": "This endpoint fetches a specific OASF record from the Agent Directory using its Content Identifier (CID). Optionally, the record's signature can be verified.", + "parameters": [ + { + "name": "cid", + "description": "Content Identifier (CID) of the record to fetch.", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "verify", + "description": "Whether to verify the record's signature.", + "in": "query", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "The request was successful.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetRecordResult" + } + } + } + }, + "400": { + "description": "A problem with the input fields has occurred. Additional state information plus metadata may be available in the response body.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequest" + }, + "example": { + "error": "InvalidRequest" + } + } + } + }, + "401": { + "$ref": "#/components/schemas/UnauthorizedError" + }, + "404": { + "description": "The record was not found.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequest" + }, + "example": { + "error": "Record not found" + } + } + } + }, + "500": { + "description": "An internal error has occurred. Additional state information plus metadata may be available in the response body.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequest" + }, + "example": { + "error": "Internal Error" + } + } + } + } + } + } + }, "/event/list": { "get": { "tags": [ diff --git a/src/types/oasf.ts b/src/types/oasf.ts new file mode 100644 index 00000000..cef7b4a5 --- /dev/null +++ b/src/types/oasf.ts @@ -0,0 +1,242 @@ +// Request/Response types for OASF records + +import { models } from 'agntcy-dir'; + +export interface UnsuccessfulPublishRecordResponseBody { + error: string; +} + +export interface SearchRecordResponseBody { + success: true; + data: PublishRecordRequestBody[]; + pagination: { + page: number; + limit: number; + total: number; + total_pages: number; + }; + filters_applied?: Record; +} + +export interface UnsuccessfulSearchRecordResponseBody { + error: string; +} + +export interface GetRecordParams { + cid: string; +} + +export interface GetRecordQuery { + verify?: boolean | string; +} + +export interface GetRecordResponseBody { + success: true; + data: PublishRecordRequestBody; + metadata: { + cid: string; + retrieved_at: string; + verified: boolean | null; + signature: VerificationResult | null; + }; +} + +export interface UnsuccessfulGetRecordResponseBody { + error: string; +} + +/** + * OASF Record type definition + */ +export interface OASFRecord { + metadata: { + version: string; + uid: string; // ← Identity field! + product: { + name: string; + vendor_name: string; + version: string; + lang?: string; + url?: string; + }; + labels?: string[]; + }; + record: { + type_uid: string; + type_name: string; + category_uid: string; + category_name: string; + class_uid: string; + class_name: string; + severity_id: number; + time: string; + }; + skills?: Array<{ + skill_id: string; + skill_name: string; + confidence?: number; + }>; + domains?: Array<{ + domain_id: string; + domain_name: string; + }>; + locators?: Array<{ + type: string; + url: string; + description?: string; + }>; + modules?: any[]; +} + +/** + * Updated interfaces for AGNTCY Directory with identity support + */ + +export interface PublishRecordRequestBody { + data: { + // Core fields + name: string; + version: string; + schema_version: string; + + // Identity field - NEW! ✨ + uid?: string; // DID, OAuth2 client_id, URL, or any unique identifier + + // Optional metadata + description?: string; + authors?: string[]; + created_at?: string; + type?: 'agent' | 'organization' | 'service' | 'mcp-server'; + + // Capabilities + skills?: Array<{ + name: string; + id: number; + }>; + + // Deployment info + locators?: Array<{ + type: string; + url: string; + description?: string; // Optional description + }>; + + // Industry/domain + domains?: Array<{ + name: string; + id: number; + }>; + + // Extensions + modules?: any[]; + }; +} + +export interface PublishRecordResponseBody { + success: true; + cid: string; + message: string; + data: { + name: string; + version: string; + cid: string; + published_at: string; + uid?: string; // Include identity in response + }; +} + +/** + * Search query interface + */ +export interface SearchRecordQuery { + // Text search + name?: string; + version?: string; + schema_version?: string; + description?: string; + + // Identity search + uid?: string; // Search by identity + + // Capability search + skill?: string; + skill_id?: number; + domain?: string; + + // Deployment search + locator?: string; + module?: string; + type?: 'agent' | 'organization' | 'service' | 'mcp-server'; + + // Pagination + page?: number; + limit?: number; +} + +/** + * Verification result interface + */ +export interface VerificationResult { + verified: boolean; + signature?: string; + signer?: string; + timestamp?: string; + error?: string; +} + +/** + * Full OASF record structure (used internally) + */ +export interface OASFRecord { + metadata: { + version: string; + uid: string; // ← IDENTITY FIELD! Can be DID, URL, OAuth2 ID, etc. + product: { + name: string; + vendor_name: string; + version: string; + lang?: string; + url?: string; + }; + labels?: string[]; + }; + record: { + type_uid: string; + type_name: string; + category_uid: string; + category_name: string; + class_uid: string; + class_name: string; + severity_id: number; + time: string; + }; + skills?: Array<{ + skill_id: string; + skill_name: string; + confidence?: number; + }>; + domains?: Array<{ + domain_id: string; + domain_name: string; + }>; + locators?: Array<{ + type: string; + url: string; + description?: string; + }>; + modules?: any[]; + authentication?: { + methods?: string[]; + did?: string; + endpoints?: any[]; + }; +} + +export const queryTypeMap: Partial> = { + name: models.search_v1.RecordQueryType.NAME, + version: models.search_v1.RecordQueryType.VERSION, + skill: models.search_v1.RecordQueryType.SKILL_NAME, + domain: models.search_v1.RecordQueryType.DOMAIN_NAME, + locator: models.search_v1.RecordQueryType.LOCATOR, + schema_version: models.search_v1.RecordQueryType.SCHEMA_VERSION, +}; diff --git a/src/types/swagger-api-types.ts b/src/types/swagger-api-types.ts index 0ce3d4aa..3eb53953 100644 --- a/src/types/swagger-api-types.ts +++ b/src/types/swagger-api-types.ts @@ -2191,4 +2191,99 @@ * type: string * format: date-time * description: Timestamp when status was last updated + * PublishRecordRequest: + * type: object + * required: + * - data + * properties: + * data: + * type: object + * required: + * - name + * - version + * - schema_version + * properties: + * name: + * type: string + * description: Name of the record + * version: + * type: string + * description: Version of the record + * schema_version: + * type: string + * description: Schema version + * description: + * type: string + * description: Optional description + * authors: + * type: array + * items: + * type: string + * description: Optional list of authors + * created_at: + * type: string + * format: date-time + * description: Optional creation timestamp + * type: + * type: string + * enum: + * - agent + * - organization + * - service + * - mcp-server + * description: Optional record type + * skills: + * type: array + * items: + * type: object + * properties: + * name: + * type: string + * id: + * type: number + * locators: + * type: array + * items: + * type: object + * properties: + * type: + * type: string + * url: + * type: string + * domains: + * type: array + * items: + * type: object + * properties: + * name: + * type: string + * id: + * type: number + * modules: + * type: array + * items: + * type: object + * PublishRecordResult: + * type: object + * properties: + * success: + * type: boolean + * example: true + * cid: + * type: string + * description: Content identifier of the published record + * message: + * type: string + * data: + * type: object + * properties: + * name: + * type: string + * version: + * type: string + * cid: + * type: string + * published_at: + * type: string + * format: date-time */