mc8yp 2.6.2 → 2.7.1
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/README.md +97 -1
- package/dist/cli.mjs +1449 -425
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1546,7 +1546,7 @@ const consola = createConsola();
|
|
|
1546
1546
|
//#endregion
|
|
1547
1547
|
//#region package.json
|
|
1548
1548
|
var name = "mc8yp";
|
|
1549
|
-
var version = "2.
|
|
1549
|
+
var version = "2.7.1";
|
|
1550
1550
|
var description$1 = "Cumulocity IoT MCP Server - Model Context Protocol integration for IoT device management";
|
|
1551
1551
|
//#endregion
|
|
1552
1552
|
//#region \0virtual:core-openapi
|
|
@@ -5390,7 +5390,7 @@ const specs = Object.freeze([
|
|
|
5390
5390
|
"operationId": "putCRLSettings",
|
|
5391
5391
|
"tags": ["Trusted certificates"],
|
|
5392
5392
|
"summary": "Add revoked certificates",
|
|
5393
|
-
"description": "> **ⓘ Info:** A certificate revocation list (CRL) is a list of digital certificates\n that have been revoked by the issuing certificate authority (CA) before expiration date.\n In Cumulocity, a CRL check can be in online or offline mode or both.\n\nAn endpoint to add revoked certificate serial numbers for offline CRL check via payload or file.\n\nFor payload, a JSON object required with list of CRL entries, for example:\n ```json\n {\n \"crls\": [\n {\n \"serialNumberInHex\": \"1000\",\n \"revocationDate\": \"2023-01-11T16:12:36.288Z\"\n }\n ]\n }\n ```\nEach entry is composed of:\n * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g As (1000)^16 == (4096)^10, So we have to enter 1000.\n If duplicate serial number exists in payload, the existing entry stays</br>\n * `revocationDate` - accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`, for example: `2023-01-11T16:12:36.288Z`.\n This is an optional parameter and defaults to the current server UTC date time if not specified in the payload.\n If specified and the date is in future then those entries will be also defaulted to current date.\n\nFor file upload, each file can hold at maximum 5000 revocation entries.\nMultiple upload is allowed.\nIn case of duplicates, the latest (last uploaded) entry is considered.\n\nSee below for a sample CSV file:\n\n| SERIAL NO. | REVOCATION DATE |\n|--|--|\n| 1000 | 2023-01-11T16:12:36.288Z |\n\n Each entry is composed of :\n * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g (1000)^16 == (4096)^10, So we have to enter 1000.\n If duplicate serial number exists in payload, the latest entry will be taken.</br>\n * revocationDate: Accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` e.g: 2023-01-11T16:12:36.288Z.\n This is an optional and will be default to current server UTC date time if not specified in payload.\n If specified and the date is in future then those entries will be skipped.\n\nThe CRL setting for offline and online check can be enabled/disabled using <kbd><a href=\"#operation/putOptionResource\">/tenant/options</a></kbd>.\nKeys are `crl.online.check.enabled` and `crl.offline.check.enabled` under the category `configuration`.\n\n<section><h5>Required roles</h5>\n(ROLE_TENANT_MANAGEMENT_ADMIN <b>OR</b> ROLE_TENANT_ADMIN) <b>AND</b> is the current tenant\n</section>\n\n**⚠️ Important:** According to CRL policy, added serial numbers cannot be reversed.\n",
|
|
5393
|
+
"description": "> **ⓘ Info:** A certificate revocation list (CRL) is a list of digital certificates\n that have been revoked by the issuing certificate authority (CA) before expiration date.\n In Cumulocity, a CRL check can be in online or offline mode or both.\n\nAn endpoint to add revoked certificate serial numbers for offline CRL check via payload or file.\nA file can be uploaded with either `PUT` or `POST`, a JSON payload is accepted by `PUT` only.\n\nFor payload, a JSON object required with list of CRL entries, for example:\n ```json\n {\n \"crls\": [\n {\n \"serialNumberInHex\": \"1000\",\n \"revocationDate\": \"2023-01-11T16:12:36.288Z\"\n }\n ]\n }\n ```\nEach entry is composed of:\n * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g As (1000)^16 == (4096)^10, So we have to enter 1000.\n If duplicate serial number exists in payload, the existing entry stays</br>\n * `revocationDate` - accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`, for example: `2023-01-11T16:12:36.288Z`.\n This is an optional parameter and defaults to the current server UTC date time if not specified in the payload.\n If specified and the date is in future then those entries will be also defaulted to current date.\n\nFor file upload, each file can hold at maximum 5000 revocation entries.\nMultiple upload is allowed.\nIn case of duplicates, the latest (last uploaded) entry is considered.\n\nSee below for a sample CSV file:\n\n| SERIAL NO. | REVOCATION DATE |\n|--|--|\n| 1000 | 2023-01-11T16:12:36.288Z |\n\n Each entry is composed of :\n * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g (1000)^16 == (4096)^10, So we have to enter 1000.\n If duplicate serial number exists in payload, the latest entry will be taken.</br>\n * revocationDate: Accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` e.g: 2023-01-11T16:12:36.288Z.\n This is an optional and will be default to current server UTC date time if not specified in payload.\n If specified and the date is in future then those entries will be skipped.\n\nThe CRL setting for offline and online check can be enabled/disabled using <kbd><a href=\"#operation/putOptionResource\">/tenant/options</a></kbd>.\nKeys are `crl.online.check.enabled` and `crl.offline.check.enabled` under the category `configuration`.\n\n<section><h5>Required roles</h5>\n(ROLE_TENANT_MANAGEMENT_ADMIN <b>OR</b> ROLE_TENANT_ADMIN) <b>AND</b> is the current tenant\n</section>\n\n**⚠️ Important:** According to CRL policy, added serial numbers cannot be reversed.\n",
|
|
5394
5394
|
"requestBody": {
|
|
5395
5395
|
"required": true,
|
|
5396
5396
|
"content": {
|
|
@@ -5408,6 +5408,25 @@ const specs = Object.freeze([
|
|
|
5408
5408
|
},
|
|
5409
5409
|
"responses": { "204": { "description": "CRLs updated successfully." } }
|
|
5410
5410
|
},
|
|
5411
|
+
"post": {
|
|
5412
|
+
"operationId": "postCRLSettings",
|
|
5413
|
+
"tags": ["Trusted certificates"],
|
|
5414
|
+
"summary": "Add revoked certificates",
|
|
5415
|
+
"description": "> **ⓘ Info:** A certificate revocation list (CRL) is a list of digital certificates\n that have been revoked by the issuing certificate authority (CA) before expiration date.\n In Cumulocity, a CRL check can be in online or offline mode or both.\n\nAn endpoint to add revoked certificate serial numbers for offline CRL check via payload or file.\nA file can be uploaded with either `PUT` or `POST`, a JSON payload is accepted by `PUT` only.\n\nFor payload, a JSON object required with list of CRL entries, for example:\n ```json\n {\n \"crls\": [\n {\n \"serialNumberInHex\": \"1000\",\n \"revocationDate\": \"2023-01-11T16:12:36.288Z\"\n }\n ]\n }\n ```\nEach entry is composed of:\n * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g As (1000)^16 == (4096)^10, So we have to enter 1000.\n If duplicate serial number exists in payload, the existing entry stays</br>\n * `revocationDate` - accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`, for example: `2023-01-11T16:12:36.288Z`.\n This is an optional parameter and defaults to the current server UTC date time if not specified in the payload.\n If specified and the date is in future then those entries will be also defaulted to current date.\n\nFor file upload, each file can hold at maximum 5000 revocation entries.\nMultiple upload is allowed.\nIn case of duplicates, the latest (last uploaded) entry is considered.\n\nSee below for a sample CSV file:\n\n| SERIAL NO. | REVOCATION DATE |\n|--|--|\n| 1000 | 2023-01-11T16:12:36.288Z |\n\n Each entry is composed of :\n * serialNumberInHex: Needs to be in `Hexadecimal Value`. e.g (1000)^16 == (4096)^10, So we have to enter 1000.\n If duplicate serial number exists in payload, the latest entry will be taken.</br>\n * revocationDate: Accepted Date format: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` e.g: 2023-01-11T16:12:36.288Z.\n This is an optional and will be default to current server UTC date time if not specified in payload.\n If specified and the date is in future then those entries will be skipped.\n\nThe CRL setting for offline and online check can be enabled/disabled using <kbd><a href=\"#operation/putOptionResource\">/tenant/options</a></kbd>.\nKeys are `crl.online.check.enabled` and `crl.offline.check.enabled` under the category `configuration`.\n\n<section><h5>Required roles</h5>\n(ROLE_TENANT_MANAGEMENT_ADMIN <b>OR</b> ROLE_TENANT_ADMIN) <b>AND</b> is the current tenant\n</section>\n\n**⚠️ Important:** According to CRL policy, added serial numbers cannot be reversed.\n",
|
|
5416
|
+
"requestBody": {
|
|
5417
|
+
"required": true,
|
|
5418
|
+
"content": { "multipart/form-data": { "schema": {
|
|
5419
|
+
"type": "object",
|
|
5420
|
+
"required": ["file"],
|
|
5421
|
+
"properties": { "file": {
|
|
5422
|
+
"description": "File to be uploaded.",
|
|
5423
|
+
"type": "string",
|
|
5424
|
+
"format": "binary"
|
|
5425
|
+
} }
|
|
5426
|
+
} } }
|
|
5427
|
+
},
|
|
5428
|
+
"responses": { "204": { "description": "CRLs updated successfully." } }
|
|
5429
|
+
},
|
|
5411
5430
|
"get": {
|
|
5412
5431
|
"operationId": "getCRLSettings",
|
|
5413
5432
|
"tags": ["Trusted certificates"],
|
|
@@ -5888,7 +5907,11 @@ const specs = Object.freeze([
|
|
|
5888
5907
|
} },
|
|
5889
5908
|
"/tenant/oauth": { "post": {
|
|
5890
5909
|
"operationId": "postLoginFormCookie",
|
|
5891
|
-
"parameters": [
|
|
5910
|
+
"parameters": [
|
|
5911
|
+
{ "$ref": "#/components/parameters/queryParam_tenant_id" },
|
|
5912
|
+
{ "$ref": "#/components/parameters/tfaCodeHeader" },
|
|
5913
|
+
{ "$ref": "#/components/parameters/acceptHeader" }
|
|
5914
|
+
],
|
|
5892
5915
|
"tags": ["Login tokens"],
|
|
5893
5916
|
"summary": "Obtain access tokens in cookies",
|
|
5894
5917
|
"description": "Obtain an OAI-Secure and XSRF tokens in cookies.\n",
|
|
@@ -5914,7 +5937,11 @@ const specs = Object.freeze([
|
|
|
5914
5937
|
} },
|
|
5915
5938
|
"/tenant/oauth/token": { "post": {
|
|
5916
5939
|
"operationId": "postLoginFormBody",
|
|
5917
|
-
"parameters": [
|
|
5940
|
+
"parameters": [
|
|
5941
|
+
{ "$ref": "#/components/parameters/queryParam_tenant_id" },
|
|
5942
|
+
{ "$ref": "#/components/parameters/tfaCodeHeader" },
|
|
5943
|
+
{ "$ref": "#/components/parameters/acceptHeader" }
|
|
5944
|
+
],
|
|
5918
5945
|
"tags": ["Login tokens"],
|
|
5919
5946
|
"summary": "Obtain an access token",
|
|
5920
5947
|
"description": "Obtain an OAI-Secure access token.",
|
|
@@ -5927,6 +5954,32 @@ const specs = Object.freeze([
|
|
|
5927
5954
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/accessToken" } } }
|
|
5928
5955
|
} }
|
|
5929
5956
|
} },
|
|
5957
|
+
"/tenant/oauth/certificate": { "post": {
|
|
5958
|
+
"operationId": "postCertificateAccessToken",
|
|
5959
|
+
"parameters": [
|
|
5960
|
+
{ "$ref": "#/components/parameters/queryParam_tenant_id" },
|
|
5961
|
+
{ "$ref": "#/components/parameters/tenantIdInHeader" },
|
|
5962
|
+
{ "$ref": "#/components/parameters/tokenResponseModeHeader" },
|
|
5963
|
+
{ "$ref": "#/components/parameters/tfaCodeHeader" },
|
|
5964
|
+
{ "$ref": "#/components/parameters/acceptHeader" }
|
|
5965
|
+
],
|
|
5966
|
+
"tags": ["Login tokens"],
|
|
5967
|
+
"summary": "Obtain an access token with a certificate",
|
|
5968
|
+
"description": "Obtain a platform access token by presenting an X.509 certificate over standard HTTPS, without requiring mutual TLS (mTLS) or MQTT. This provides a REST-native, standard-port alternative for certificate-authenticated clients that cannot use MQTT or the dedicated mTLS endpoint.\n\nThe request body contains the leaf certificate, or the full certificate chain, in PEM format. By default the returned access token is encrypted as a JWE with the public key from the presented certificate, so that only the holder of the corresponding private key can decrypt it and use it as a `Bearer` token for subsequent REST API requests. The response format can be controlled with the `X-Cumulocity-Token-Response-Mode` header. JWE mode requires the certificate to use an RSA key, because the platform encrypts the token with `RSA-OAEP-256`; clients presenting a certificate with a non-RSA key must request a plain JWT with the `X-Cumulocity-Token-Response-Mode: jwt` header.\n\nThe platform derives the identity from the certificate subject, validates the certificate material against the tenant-trusted certificate authorities (including validity dates and revocation checks where configured) and confirms that the certificate is authorized for the resolved user. The tenant context must be established from the request before validation, for example through the `tenant_id` query parameter or the `X-Cumulocity-TenantId` header. The certificate is not used as the primary tenant identification mechanism.\n\nIf the resolved certificate identity is ambiguous, for example when the same certificate common name could resolve to both a regular user and a device user, the request is rejected and no token is issued. Untrusted, expired, revoked, malformed, unsupported or unmapped certificates are rejected as well.\n\nIf two-factor authentication (TFA) is active for the resolved user, it must be passed with the request as well, because the certificate replaces the password, not the second factor. Send the current TFA code in the `X-Cumulocity-TFA-Code` header. Both the TOTP and the SMS strategy are supported, in the same way as for the OAI-Secure login. Device users are never asked for a second factor.\n",
|
|
5969
|
+
"requestBody": {
|
|
5970
|
+
"required": true,
|
|
5971
|
+
"content": { "application/x-pem-file": { "schema": {
|
|
5972
|
+
"type": "string",
|
|
5973
|
+
"format": "binary",
|
|
5974
|
+
"description": "Leaf certificate or full certificate chain in PEM format.",
|
|
5975
|
+
"example": "-----BEGIN CERTIFICATE-----\nMIIDTzCCAjegAwIBAgIUB1a5GM9ubBpN5tyU7YO8D3C8zUUwDQYJKoZIhvcNAQEL\n...\nzngrsOfyKr8YYlDRy6RiAR2HQode00Hs4WoakfuTpaISZIs=\n-----END CERTIFICATE-----"
|
|
5976
|
+
} } }
|
|
5977
|
+
},
|
|
5978
|
+
"responses": { "200": {
|
|
5979
|
+
"description": "The access token is sent in the response. By default it is a JWE encrypted with the public key from the presented certificate.",
|
|
5980
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/certificateAccessToken" } } }
|
|
5981
|
+
} }
|
|
5982
|
+
} },
|
|
5930
5983
|
"/tenant/options": {
|
|
5931
5984
|
"post": {
|
|
5932
5985
|
"operationId": "postOptionCollectionResource",
|
|
@@ -6197,6 +6250,26 @@ const specs = Object.freeze([
|
|
|
6197
6250
|
"example": "t07007007"
|
|
6198
6251
|
}
|
|
6199
6252
|
},
|
|
6253
|
+
"tokenResponseModeHeader": {
|
|
6254
|
+
"name": "X-Cumulocity-Token-Response-Mode",
|
|
6255
|
+
"in": "header",
|
|
6256
|
+
"description": "Optional. Selects the format of the returned access token. `jwe` (default) returns the access token as a JWE encrypted with the public key from the presented certificate (RSA-OAEP-256 for key encryption and AES-256-GCM for content encryption), so that only the holder of the corresponding private key can decrypt it; it requires the certificate to use an RSA key. `jwt` returns a plain, unencrypted JWT and is rejected with a 403 error unless plain JWT responses are enabled for the platform or the certificate does not use an RSA key. Unsupported values are rejected with a 400 error.\n",
|
|
6257
|
+
"schema": {
|
|
6258
|
+
"type": "string",
|
|
6259
|
+
"enum": ["jwe", "jwt"],
|
|
6260
|
+
"default": "jwe",
|
|
6261
|
+
"example": "jwe"
|
|
6262
|
+
}
|
|
6263
|
+
},
|
|
6264
|
+
"tfaCodeHeader": {
|
|
6265
|
+
"name": "X-Cumulocity-TFA-Code",
|
|
6266
|
+
"in": "header",
|
|
6267
|
+
"description": "Optional. Current TFA code of the user, if a TFA code is required to log in. For form-based OAI-Secure login, the code can be sent as the `tfa_code` field in the request body; this header is useful for requests that cannot include that field (for example when the request body is a certificate chain). If both are sent, the request body field takes precedence.\n",
|
|
6268
|
+
"schema": {
|
|
6269
|
+
"type": "string",
|
|
6270
|
+
"example": "123433"
|
|
6271
|
+
}
|
|
6272
|
+
},
|
|
6200
6273
|
"alarmId": {
|
|
6201
6274
|
"name": "id",
|
|
6202
6275
|
"in": "path",
|
|
@@ -8427,6 +8500,17 @@ const specs = Object.freeze([
|
|
|
8427
8500
|
}
|
|
8428
8501
|
} }
|
|
8429
8502
|
},
|
|
8503
|
+
"jweUnsupportedCertificateKeyUnprocessableEntity": {
|
|
8504
|
+
"description": "The presented certificate does not use an RSA key, so the access token cannot be encrypted as a JWE. Request a plain JWT with the `X-Cumulocity-Token-Response-Mode: jwt` header instead.\n",
|
|
8505
|
+
"content": { "application/vnd.com.nsn.cumulocity.error+json": {
|
|
8506
|
+
"schema": { "$ref": "#/components/schemas/error" },
|
|
8507
|
+
"example": {
|
|
8508
|
+
"error": "certificate-token/Unprocessable Entity",
|
|
8509
|
+
"message": "Only RSA certificates are supported for JWE mode. Use header X-Cumulocity-Token-Response-Mode: jwt for non-RSA certificates.",
|
|
8510
|
+
"info": "https://www.cumulocity.com/guides/reference-guide/#error_reporting"
|
|
8511
|
+
}
|
|
8512
|
+
} }
|
|
8513
|
+
},
|
|
8430
8514
|
"unableToParseCRLEntries": {
|
|
8431
8515
|
"description": "Unsupported date time format.",
|
|
8432
8516
|
"content": { "application/vnd.com.nsn.cumulocity.error+json": {
|
|
@@ -14584,6 +14668,33 @@ const specs = Object.freeze([
|
|
|
14584
14668
|
}
|
|
14585
14669
|
}
|
|
14586
14670
|
},
|
|
14671
|
+
"certificateAccessToken": {
|
|
14672
|
+
"description": "Access token obtained by presenting an X.509 certificate. By default the token is returned as a JWE encrypted with the public key from the presented certificate, so that only the holder of the corresponding private key can decrypt it. This requires the certificate to use an RSA key, because the platform encrypts the token with `RSA-OAEP-256`.\n\nA plain, unencrypted JWT can be requested with the `X-Cumulocity-Token-Response-Mode: jwt` header, but such requests are rejected with a 403 error unless plain JWT responses have been explicitly enabled for the platform. Clients whose certificate does not use an RSA key are exempt from that restriction, as JWE mode is not available to them: they may always request `jwt`, and must do so, because a request left in the default `jwe` mode is rejected with a 422 error.\n",
|
|
14673
|
+
"type": "object",
|
|
14674
|
+
"properties": {
|
|
14675
|
+
"access_token": {
|
|
14676
|
+
"description": "The access token generated by the Cumulocity platform. A JWE compact-serialized, encrypted JWT when `response_mode` is `jwe`, or a plain, unencrypted JWT when `response_mode` is `jwt`.",
|
|
14677
|
+
"type": "string",
|
|
14678
|
+
"example": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.g_hEwksO1Ax8Qn7HoN-BVeBoa8FXe0kpyk_XdcSmxvcM5_P296JXXtoHISr_DD_M...48V95cLzdVlCyoQ"
|
|
14679
|
+
},
|
|
14680
|
+
"token_type": {
|
|
14681
|
+
"description": "The type of the returned token.",
|
|
14682
|
+
"type": "string",
|
|
14683
|
+
"example": "Bearer"
|
|
14684
|
+
},
|
|
14685
|
+
"response_mode": {
|
|
14686
|
+
"description": "The format of the returned access token. `jwe` unless a plain JWT was requested with the `X-Cumulocity-Token-Response-Mode` header and permitted.",
|
|
14687
|
+
"type": "string",
|
|
14688
|
+
"enum": ["jwe", "jwt"],
|
|
14689
|
+
"example": "jwe"
|
|
14690
|
+
},
|
|
14691
|
+
"expires_in": {
|
|
14692
|
+
"description": "The token lifetime in seconds.",
|
|
14693
|
+
"type": "integer",
|
|
14694
|
+
"example": 3600
|
|
14695
|
+
}
|
|
14696
|
+
}
|
|
14697
|
+
},
|
|
14587
14698
|
"SystemOptionCollection": {
|
|
14588
14699
|
"description": "All available system options of the tenant.",
|
|
14589
14700
|
"type": "object",
|
|
@@ -58548,17 +58659,17 @@ function buildMcpTools(server) {
|
|
|
58548
58659
|
return tools;
|
|
58549
58660
|
}
|
|
58550
58661
|
/**
|
|
58551
|
-
* Build the per-connection namespace list: core as `c8y
|
|
58552
|
-
*
|
|
58553
|
-
*
|
|
58554
|
-
* spec is used as the fallback.
|
|
58555
|
-
*
|
|
58662
|
+
* Build the per-connection namespace list: core as `c8y`, one namespace per
|
|
58663
|
+
* available service, then one per connection-supplied external MCP server. A
|
|
58664
|
+
* service exposing an MCP server becomes an MCP namespace (its spec is skipped)
|
|
58665
|
+
* unless opted out via `noMcp` — then its spec is used as the fallback.
|
|
58666
|
+
* Operations blocked by the connection policy are omitted from OpenAPI
|
|
58667
|
+
* namespaces; path templates are matched as-is.
|
|
58556
58668
|
* @param resolved
|
|
58557
|
-
* @param
|
|
58558
|
-
* @param allowRules
|
|
58559
|
-
* @param noMcp Per-connection MCP-wrapping opt-out.
|
|
58669
|
+
* @param options Per-connection policy, opt-outs, and external servers.
|
|
58560
58670
|
*/
|
|
58561
|
-
function buildNamespaces(resolved,
|
|
58671
|
+
function buildNamespaces(resolved, options = {}) {
|
|
58672
|
+
const { restrictions = [], allowRules = [], noMcp, externalServers = [] } = options;
|
|
58562
58673
|
const visibleOperations = (spec) => deriveOperations(spec).filter((op) => !evaluateAccessPolicy(restrictions, allowRules, op.method, op.path).blocked);
|
|
58563
58674
|
const namespaces = [{
|
|
58564
58675
|
kind: "openapi",
|
|
@@ -58601,6 +58712,31 @@ function buildNamespaces(resolved, restrictions = [], allowRules = [], noMcp) {
|
|
|
58601
58712
|
});
|
|
58602
58713
|
}
|
|
58603
58714
|
}
|
|
58715
|
+
for (const external of externalServers) {
|
|
58716
|
+
const name = external.config.name;
|
|
58717
|
+
if (RESERVED_NAMESPACES.has(name) || used.has(name)) {
|
|
58718
|
+
consola.warn(`[codemode] external MCP server "${name}" (${external.config.url}) maps to a namespace that is ${RESERVED_NAMESPACES.has(name) ? "reserved" : "already used by this tenant"} — skipping this server.`);
|
|
58719
|
+
continue;
|
|
58720
|
+
}
|
|
58721
|
+
used.add(name);
|
|
58722
|
+
const server = {
|
|
58723
|
+
contextPath: name,
|
|
58724
|
+
appLabel: name,
|
|
58725
|
+
mcpName: name,
|
|
58726
|
+
description: external.config.description ?? external.instructions,
|
|
58727
|
+
url: external.config.url,
|
|
58728
|
+
sendAuthentication: false,
|
|
58729
|
+
tools: external.tools
|
|
58730
|
+
};
|
|
58731
|
+
namespaces.push({
|
|
58732
|
+
kind: "mcp",
|
|
58733
|
+
name,
|
|
58734
|
+
specKey: name,
|
|
58735
|
+
external: external.config,
|
|
58736
|
+
server,
|
|
58737
|
+
tools: buildMcpTools(server)
|
|
58738
|
+
});
|
|
58739
|
+
}
|
|
58604
58740
|
return namespaces;
|
|
58605
58741
|
}
|
|
58606
58742
|
/**
|
|
@@ -58664,7 +58800,12 @@ function createCodeModeGuidePrompt() {
|
|
|
58664
58800
|
const restrictions = c8yMcpServer.ctx.custom?.restrictions ?? [];
|
|
58665
58801
|
const allowRules = c8yMcpServer.ctx.custom?.allowRules ?? [];
|
|
58666
58802
|
const resolvedSpecs = c8yMcpServer.ctx.custom?.specs;
|
|
58667
|
-
const namespaceNames = resolvedSpecs ? buildNamespaces(resolvedSpecs,
|
|
58803
|
+
const namespaceNames = [...resolvedSpecs ? buildNamespaces(resolvedSpecs, {
|
|
58804
|
+
restrictions,
|
|
58805
|
+
allowRules
|
|
58806
|
+
}).map((ns) => ns.name) : ["c8y"], ...(c8yMcpServer.ctx.custom?.externalMcpServers ?? []).map((s) => s.name)];
|
|
58807
|
+
const externalServers = c8yMcpServer.ctx.custom?.externalMcpServers ?? [];
|
|
58808
|
+
const externalLine = externalServers.length > 0 ? `\n- ${externalServers.map((s) => `\`${s.name}\``).join(", ")} ${externalServers.length === 1 ? "is an" : "are"} external MCP server${externalServers.length === 1 ? "" : "s"} configured for THIS connection — not part of the tenant. Same typed-method surface; describe and search work identically.` : "";
|
|
58668
58809
|
const policyLines = [...restrictions.map((rule) => `- deny: \`${rule.source}\``), ...allowRules.map((rule) => `- allow: \`${rule.source}\``)];
|
|
58669
58810
|
const restrictionSection = policyLines.length > 0 ? `\n## Current Connection Access Policy\n${policyLines.join("\n")}\n\nOperations blocked by these rules are omitted from discovery (search/describe) entirely, and any live request that matches a deny rule (or misses the allow list) fails before reaching the tenant.\n` : "";
|
|
58670
58811
|
const sandboxSection = c8yMcpServer.ctx.custom?.env === "server" && c8yMcpServer.ctx.custom?.enableSandbox ? `\n## Sandbox (scratch compute)
|
|
@@ -58707,7 +58848,7 @@ declare const docs: {
|
|
|
58707
58848
|
API namespaces currently visible: ${namespaceNames.map((n) => `\`${n}\``).join(", ")}.
|
|
58708
58849
|
|
|
58709
58850
|
- \`c8y\` is the Cumulocity core REST surface (inventory, alarms, events, measurements, identity, device control, users, tenants, audit). Always present.
|
|
58710
|
-
- Each additional namespace is a microservice available on the current tenant (e.g. \`dtm\`). A namespace exists only when the service is actually reachable
|
|
58851
|
+
- Each additional namespace is a microservice available on the current tenant (e.g. \`dtm\`). A namespace exists only when the service is actually reachable.${externalLine}
|
|
58711
58852
|
- Every namespace has one typed method per API operation. The namespaces are the complete surface — there is no raw-request escape hatch. If a method seems missing, re-search with different wording; if it truly does not exist, report that instead of improvising.
|
|
58712
58853
|
- Method inputs are a single flat object: path/query/header parameters as top-level keys, the request payload under \`body\`.
|
|
58713
58854
|
|
|
@@ -77296,7 +77437,18 @@ const indexCache$1 = /* @__PURE__ */ new WeakMap();
|
|
|
77296
77437
|
function getMethodIndex(cacheKey, items) {
|
|
77297
77438
|
const cached = indexCache$1.get(cacheKey);
|
|
77298
77439
|
if (cached) return cached;
|
|
77299
|
-
const
|
|
77440
|
+
const index = buildMethodIndex(items());
|
|
77441
|
+
indexCache$1.set(cacheKey, index);
|
|
77442
|
+
return index;
|
|
77443
|
+
}
|
|
77444
|
+
/**
|
|
77445
|
+
* Build an index without caching it. Used for connection-scoped method sets
|
|
77446
|
+
* (external MCP namespaces): those cannot enter the per-tenant cache, and
|
|
77447
|
+
* indexing a few hundred short documents costs single-digit milliseconds
|
|
77448
|
+
* against a codemode call measured in seconds.
|
|
77449
|
+
* @param list
|
|
77450
|
+
*/
|
|
77451
|
+
function buildMethodIndex(list) {
|
|
77300
77452
|
const mini = new MiniSearch({
|
|
77301
77453
|
idField: "target",
|
|
77302
77454
|
fields: [
|
|
@@ -77308,12 +77460,10 @@ function getMethodIndex(cacheKey, items) {
|
|
|
77308
77460
|
tokenize
|
|
77309
77461
|
});
|
|
77310
77462
|
mini.addAll([...list]);
|
|
77311
|
-
|
|
77463
|
+
return {
|
|
77312
77464
|
mini,
|
|
77313
77465
|
methods: new Map(list.map((m) => [m.target, m]))
|
|
77314
77466
|
};
|
|
77315
|
-
indexCache$1.set(cacheKey, index);
|
|
77316
|
-
return index;
|
|
77317
77467
|
}
|
|
77318
77468
|
/**
|
|
77319
77469
|
* Search the method index with one query or several phrasings at once
|
|
@@ -77542,7 +77692,7 @@ function truncateLine(text) {
|
|
|
77542
77692
|
return flattened.length > 220 ? `${flattened.slice(0, 220)}…` : flattened;
|
|
77543
77693
|
}
|
|
77544
77694
|
const SANDBOX_METHOD_COUNT = (SANDBOX_INTERFACE_TS.match(/^ {2}\w+:/gm) ?? []).length;
|
|
77545
|
-
function renderOverview(namespaces, sandboxEnabled) {
|
|
77695
|
+
function renderOverview(namespaces, sandboxEnabled, externalFailures) {
|
|
77546
77696
|
const lines = ["Available namespaces on this tenant (do not assume capabilities from prior knowledge — search each relevant domain):"];
|
|
77547
77697
|
for (const ns of namespaces) if (ns.kind === "openapi") {
|
|
77548
77698
|
const info = ns.spec.info;
|
|
@@ -77550,9 +77700,14 @@ function renderOverview(namespaces, sandboxEnabled) {
|
|
|
77550
77700
|
lines.push(`- ${ns.name}${info?.title ? ` — ${info.title}` : ""} (${ns.operations.length} methods)${short ? `: ${short}` : ""}`);
|
|
77551
77701
|
} else {
|
|
77552
77702
|
const short = truncateLine(ns.server.description);
|
|
77553
|
-
|
|
77703
|
+
const label = ns.external ? `EXTERNAL MCP server at ${ns.external.url} — configured for this connection, NOT part of this tenant` : ns.server.mcpName;
|
|
77704
|
+
lines.push(`- ${ns.name} — ${label} (${ns.tools.length} methods)${short ? `: ${short}` : ""}`);
|
|
77554
77705
|
}
|
|
77555
77706
|
if (sandboxEnabled) lines.push(`- sandbox — in-memory shell + virtual filesystem (${SANDBOX_METHOD_COUNT} methods): jq/awk/grep/sed/sort/sqlite over data you fetched; no network, no host FS. codemode.describe("sandbox") for its methods.`);
|
|
77707
|
+
if (externalFailures.length > 0) {
|
|
77708
|
+
lines.push("", "Configured but unreachable right now (report this to the user; retrying may work if it is transient):");
|
|
77709
|
+
for (const failure of externalFailures) lines.push(`- ${failure.name} (${failure.url}): ${failure.reason}`);
|
|
77710
|
+
}
|
|
77556
77711
|
lines.push("", "Workflow:", "- codemode.search(\"keywords\") — find methods by name/path/summary (top 20 by score)", "- codemode.describe(\"<namespace>.<method>\") — types and docs for one method", "- docs.search(\"keywords\") / docs.read(id) — documentation topics (domain query languages, concepts)", "- <namespace>.<method>({ ...params, body }) — call the API");
|
|
77557
77712
|
return lines.join("\n");
|
|
77558
77713
|
}
|
|
@@ -77584,6 +77739,7 @@ function renderMcpTool(namespace, tool) {
|
|
|
77584
77739
|
outputSchema: tool.outputSchema
|
|
77585
77740
|
});
|
|
77586
77741
|
const lines = [`${namespace.name}.${tool.name}`];
|
|
77742
|
+
if (namespace.external) lines.push("", `External MCP server (${namespace.external.url}) — configured for this MCP connection, not part of this tenant. Calls go to that host with its own credentials; tenant credentials are never sent.`);
|
|
77587
77743
|
if (tool.description) lines.push("", tool.description);
|
|
77588
77744
|
lines.push("", "```ts", signature, "", types, "```");
|
|
77589
77745
|
return lines.join("\n");
|
|
@@ -77624,14 +77780,15 @@ function renderSearchRedirect(target, namespaces, methodIndex) {
|
|
|
77624
77780
|
* @param namespaces
|
|
77625
77781
|
* @param methodIndex
|
|
77626
77782
|
* @param target
|
|
77627
|
-
* @param
|
|
77783
|
+
* @param options Run-scoped extras for the overview.
|
|
77628
77784
|
*/
|
|
77629
|
-
function describeTarget(namespaces, methodIndex, target,
|
|
77785
|
+
function describeTarget(namespaces, methodIndex, target, options = {}) {
|
|
77786
|
+
const { sandboxEnabled = false, externalFailures = [] } = options;
|
|
77630
77787
|
const trimmed = target?.trim() ?? "";
|
|
77631
77788
|
if (trimmed === "") return {
|
|
77632
77789
|
target: "",
|
|
77633
77790
|
kind: "overview",
|
|
77634
|
-
content: renderOverview(namespaces, sandboxEnabled)
|
|
77791
|
+
content: renderOverview(namespaces, sandboxEnabled, externalFailures)
|
|
77635
77792
|
};
|
|
77636
77793
|
const [maybeNamespace, maybeMethod] = trimmed.includes(".") ? [trimmed.slice(0, trimmed.indexOf(".")), trimmed.slice(trimmed.indexOf(".") + 1)] : [trimmed, void 0];
|
|
77637
77794
|
if (sandboxEnabled && maybeNamespace === "sandbox") return {
|
|
@@ -77808,29 +77965,29 @@ function createJustBashAdapter() {
|
|
|
77808
77965
|
}
|
|
77809
77966
|
//#endregion
|
|
77810
77967
|
//#region src/codemode/sandbox/index.ts
|
|
77811
|
-
const IDLE_TTL_MS = 900 * 1e3;
|
|
77812
|
-
const sessions = /* @__PURE__ */ new Map();
|
|
77813
|
-
function armIdleTimer(sessionId) {
|
|
77814
|
-
const session = sessions.get(sessionId);
|
|
77968
|
+
const IDLE_TTL_MS$1 = 900 * 1e3;
|
|
77969
|
+
const sessions$1 = /* @__PURE__ */ new Map();
|
|
77970
|
+
function armIdleTimer$1(sessionId) {
|
|
77971
|
+
const session = sessions$1.get(sessionId);
|
|
77815
77972
|
if (!session) return;
|
|
77816
77973
|
if (session.timer) clearTimeout(session.timer);
|
|
77817
|
-
session.timer = setTimeout(() => evictSandboxSession(sessionId, "idle-timeout"), IDLE_TTL_MS);
|
|
77974
|
+
session.timer = setTimeout(() => evictSandboxSession(sessionId, "idle-timeout"), IDLE_TTL_MS$1);
|
|
77818
77975
|
session.timer.unref?.();
|
|
77819
77976
|
}
|
|
77820
77977
|
function getSessionAdapter(sessionId) {
|
|
77821
|
-
let session = sessions.get(sessionId);
|
|
77978
|
+
let session = sessions$1.get(sessionId);
|
|
77822
77979
|
if (!session) {
|
|
77823
77980
|
session = { adapter: createJustBashAdapter() };
|
|
77824
|
-
sessions.set(sessionId, session);
|
|
77981
|
+
sessions$1.set(sessionId, session);
|
|
77825
77982
|
}
|
|
77826
|
-
armIdleTimer(sessionId);
|
|
77983
|
+
armIdleTimer$1(sessionId);
|
|
77827
77984
|
return session.adapter;
|
|
77828
77985
|
}
|
|
77829
77986
|
function resetSessionAdapter(sessionId) {
|
|
77830
|
-
const session = sessions.get(sessionId);
|
|
77987
|
+
const session = sessions$1.get(sessionId);
|
|
77831
77988
|
session?.adapter.dispose?.();
|
|
77832
77989
|
if (session) session.adapter = createJustBashAdapter();
|
|
77833
|
-
armIdleTimer(sessionId);
|
|
77990
|
+
armIdleTimer$1(sessionId);
|
|
77834
77991
|
}
|
|
77835
77992
|
/**
|
|
77836
77993
|
* Drop a session's sandbox and its timer. Called by the clean-close (DELETE)
|
|
@@ -77839,18 +77996,18 @@ function resetSessionAdapter(sessionId) {
|
|
|
77839
77996
|
* @param reason - Why the sandbox is being dropped; included in the eviction log line.
|
|
77840
77997
|
*/
|
|
77841
77998
|
function evictSandboxSession(sessionId, reason) {
|
|
77842
|
-
const session = sessions.get(sessionId);
|
|
77999
|
+
const session = sessions$1.get(sessionId);
|
|
77843
78000
|
if (!session) return;
|
|
77844
78001
|
if (session.timer) clearTimeout(session.timer);
|
|
77845
78002
|
session.adapter.dispose?.();
|
|
77846
|
-
sessions.delete(sessionId);
|
|
78003
|
+
sessions$1.delete(sessionId);
|
|
77847
78004
|
consola.info(`[sandbox] evicted workspace for session ${sessionId} (reason: ${reason})`);
|
|
77848
78005
|
}
|
|
77849
78006
|
/**
|
|
77850
78007
|
* Evict every session (process exit, test cleanup).
|
|
77851
78008
|
*/
|
|
77852
78009
|
function disposeAllSandboxSessions() {
|
|
77853
|
-
for (const sessionId of [...sessions.keys()]) evictSandboxSession(sessionId, "shutdown");
|
|
78010
|
+
for (const sessionId of [...sessions$1.keys()]) evictSandboxSession(sessionId, "shutdown");
|
|
77854
78011
|
}
|
|
77855
78012
|
/**
|
|
77856
78013
|
* Build the `sandbox` host-module leaf for one codemode run: the full Flue
|
|
@@ -77903,6 +78060,414 @@ function buildSandboxApi(sessionId) {
|
|
|
77903
78060
|
};
|
|
77904
78061
|
}
|
|
77905
78062
|
//#endregion
|
|
78063
|
+
//#region src/utils/mcp-client.ts
|
|
78064
|
+
const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
78065
|
+
const REQUEST_TIMEOUT_MS = 3e4;
|
|
78066
|
+
var McpHttpClient = class {
|
|
78067
|
+
#url;
|
|
78068
|
+
#fetch;
|
|
78069
|
+
#timeoutMs;
|
|
78070
|
+
#sessionId;
|
|
78071
|
+
#nextId = 1;
|
|
78072
|
+
#initialized;
|
|
78073
|
+
constructor(options) {
|
|
78074
|
+
this.#url = options.url;
|
|
78075
|
+
this.#fetch = options.fetch;
|
|
78076
|
+
this.#timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
78077
|
+
}
|
|
78078
|
+
/**
|
|
78079
|
+
* Initialize the session (idempotent — concurrent callers share one
|
|
78080
|
+
* handshake). Advertises no client capabilities: no elicitation, no
|
|
78081
|
+
* sampling, no roots.
|
|
78082
|
+
*/
|
|
78083
|
+
initialize() {
|
|
78084
|
+
this.#initialized ??= this.#doInitialize();
|
|
78085
|
+
return this.#initialized;
|
|
78086
|
+
}
|
|
78087
|
+
async #doInitialize() {
|
|
78088
|
+
const result = await this.#request("initialize", {
|
|
78089
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
78090
|
+
capabilities: {},
|
|
78091
|
+
clientInfo: {
|
|
78092
|
+
name: "mc8yp",
|
|
78093
|
+
version: "0.0.0"
|
|
78094
|
+
}
|
|
78095
|
+
});
|
|
78096
|
+
await this.#notify("notifications/initialized");
|
|
78097
|
+
return {
|
|
78098
|
+
serverName: result?.serverInfo?.name,
|
|
78099
|
+
serverVersion: result?.serverInfo?.version,
|
|
78100
|
+
instructions: result?.instructions
|
|
78101
|
+
};
|
|
78102
|
+
}
|
|
78103
|
+
/**
|
|
78104
|
+
* List every tool, following pagination cursors.
|
|
78105
|
+
*/
|
|
78106
|
+
async listTools() {
|
|
78107
|
+
await this.initialize();
|
|
78108
|
+
const tools = [];
|
|
78109
|
+
let cursor;
|
|
78110
|
+
do {
|
|
78111
|
+
const result = await this.#request("tools/list", cursor ? { cursor } : {});
|
|
78112
|
+
tools.push(...result?.tools ?? []);
|
|
78113
|
+
cursor = result?.nextCursor;
|
|
78114
|
+
} while (cursor);
|
|
78115
|
+
return tools;
|
|
78116
|
+
}
|
|
78117
|
+
/**
|
|
78118
|
+
* Call a tool and unwrap the result: structured content when present,
|
|
78119
|
+
* otherwise joined text content (JSON-parsed when possible). `isError`
|
|
78120
|
+
* results throw with the server's message.
|
|
78121
|
+
* @param name
|
|
78122
|
+
* @param args
|
|
78123
|
+
*/
|
|
78124
|
+
async callTool(name, args) {
|
|
78125
|
+
await this.initialize();
|
|
78126
|
+
const result = await this.#request("tools/call", {
|
|
78127
|
+
name,
|
|
78128
|
+
arguments: args && typeof args === "object" ? args : {}
|
|
78129
|
+
});
|
|
78130
|
+
if (!result || typeof result !== "object") return result;
|
|
78131
|
+
if (result.isError) {
|
|
78132
|
+
const message = (result.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("\n") || `MCP tool "${name}" failed`;
|
|
78133
|
+
throw new Error(message);
|
|
78134
|
+
}
|
|
78135
|
+
if (result.structuredContent != null) return result.structuredContent;
|
|
78136
|
+
const content = result.content ?? [];
|
|
78137
|
+
if (content.length === 0 || !content.every((c) => c.type === "text")) return result;
|
|
78138
|
+
const text = content.map((c) => c.text ?? "").join("\n");
|
|
78139
|
+
try {
|
|
78140
|
+
return JSON.parse(text);
|
|
78141
|
+
} catch {
|
|
78142
|
+
return text;
|
|
78143
|
+
}
|
|
78144
|
+
}
|
|
78145
|
+
/**
|
|
78146
|
+
* Best-effort session teardown. Never throws.
|
|
78147
|
+
*/
|
|
78148
|
+
async close() {
|
|
78149
|
+
if (!this.#sessionId) return;
|
|
78150
|
+
try {
|
|
78151
|
+
await this.#fetch(this.#url, {
|
|
78152
|
+
method: "DELETE",
|
|
78153
|
+
headers: { "mcp-session-id": this.#sessionId }
|
|
78154
|
+
});
|
|
78155
|
+
} catch {}
|
|
78156
|
+
this.#sessionId = void 0;
|
|
78157
|
+
this.#initialized = void 0;
|
|
78158
|
+
}
|
|
78159
|
+
async #notify(method) {
|
|
78160
|
+
await this.#post({
|
|
78161
|
+
jsonrpc: "2.0",
|
|
78162
|
+
method
|
|
78163
|
+
});
|
|
78164
|
+
}
|
|
78165
|
+
async #request(method, params) {
|
|
78166
|
+
const id = this.#nextId++;
|
|
78167
|
+
const response = await this.#post({
|
|
78168
|
+
jsonrpc: "2.0",
|
|
78169
|
+
id,
|
|
78170
|
+
method,
|
|
78171
|
+
params
|
|
78172
|
+
});
|
|
78173
|
+
const message = await this.#readResponse(response, id);
|
|
78174
|
+
if (message.error) throw new Error(`MCP ${method} failed: ${message.error.message}`);
|
|
78175
|
+
return message.result;
|
|
78176
|
+
}
|
|
78177
|
+
async #post(payload) {
|
|
78178
|
+
const controller = new AbortController();
|
|
78179
|
+
const timer = setTimeout(() => controller.abort(), this.#timeoutMs);
|
|
78180
|
+
try {
|
|
78181
|
+
const response = await this.#fetch(this.#url, {
|
|
78182
|
+
method: "POST",
|
|
78183
|
+
headers: {
|
|
78184
|
+
"content-type": "application/json",
|
|
78185
|
+
"accept": "application/json, text/event-stream",
|
|
78186
|
+
...this.#sessionId ? { "mcp-session-id": this.#sessionId } : {}
|
|
78187
|
+
},
|
|
78188
|
+
body: JSON.stringify(payload),
|
|
78189
|
+
signal: controller.signal
|
|
78190
|
+
});
|
|
78191
|
+
this.#sessionId ??= response.headers.get("mcp-session-id") ?? void 0;
|
|
78192
|
+
if (!response.ok && response.status !== 202) throw new Error(`MCP endpoint responded with ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`);
|
|
78193
|
+
return response;
|
|
78194
|
+
} finally {
|
|
78195
|
+
clearTimeout(timer);
|
|
78196
|
+
}
|
|
78197
|
+
}
|
|
78198
|
+
/**
|
|
78199
|
+
* Read the JSON-RPC response for `id` from a plain-JSON or SSE-framed
|
|
78200
|
+
* response body. Server→client requests encountered on the stream are
|
|
78201
|
+
* declined immediately (fire-and-forget error response) — mc8yp does not
|
|
78202
|
+
* forward elicitation or sampling.
|
|
78203
|
+
* @param response
|
|
78204
|
+
* @param id
|
|
78205
|
+
*/
|
|
78206
|
+
async #readResponse(response, id) {
|
|
78207
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
78208
|
+
const text = await response.text();
|
|
78209
|
+
const messages = contentType.includes("text/event-stream") ? text.split(/\n\n/).flatMap((event) => event.split("\n").filter((line) => line.startsWith("data: ")).map((line) => line.slice(6))).filter(Boolean).map((data) => JSON.parse(data)) : text.trim() ? [JSON.parse(text)] : [];
|
|
78210
|
+
for (const message of messages) {
|
|
78211
|
+
if (message.method && message.id !== void 0) {
|
|
78212
|
+
this.#declineServerRequest(message).catch(() => void 0);
|
|
78213
|
+
continue;
|
|
78214
|
+
}
|
|
78215
|
+
if (message.id === id) return message;
|
|
78216
|
+
}
|
|
78217
|
+
throw new Error(`MCP endpoint returned no response for request ${id}`);
|
|
78218
|
+
}
|
|
78219
|
+
async #declineServerRequest(request) {
|
|
78220
|
+
consola.warn(`[mcp-client] declining server-initiated request "${request.method}" — mc8yp does not forward elicitation or sampling.`);
|
|
78221
|
+
try {
|
|
78222
|
+
await this.#post({
|
|
78223
|
+
jsonrpc: "2.0",
|
|
78224
|
+
id: request.id,
|
|
78225
|
+
error: {
|
|
78226
|
+
code: -32601,
|
|
78227
|
+
message: `mc8yp does not forward ${request.method === "elicitation/create" ? "elicitation" : request.method === "sampling/createMessage" ? "sampling" : "server-initiated"} requests. The tool cannot interact with the user through this connection.`
|
|
78228
|
+
}
|
|
78229
|
+
});
|
|
78230
|
+
} catch {}
|
|
78231
|
+
}
|
|
78232
|
+
};
|
|
78233
|
+
//#endregion
|
|
78234
|
+
//#region src/utils/external-mcp.ts
|
|
78235
|
+
const NAMESPACE_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
78236
|
+
/**
|
|
78237
|
+
* Parse external MCP server entries from header values or CLI flags. Every
|
|
78238
|
+
* entry is a JSON object `{ name, url, token?, headers?, description? }`, or a
|
|
78239
|
+
* JSON array of them.
|
|
78240
|
+
*
|
|
78241
|
+
* Validation is strict and fail-loud rather than skip-and-continue: a
|
|
78242
|
+
* malformed entry means the operator intended a server the agent would
|
|
78243
|
+
* otherwise silently not have, so callers turn `failedEntries` into a 400
|
|
78244
|
+
* (server mode) or a startup error (CLI).
|
|
78245
|
+
* @param sources Raw JSON texts, one per entry.
|
|
78246
|
+
*/
|
|
78247
|
+
function parseExternalMcpServers(sources) {
|
|
78248
|
+
const servers = [];
|
|
78249
|
+
const failedEntries = [];
|
|
78250
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
78251
|
+
for (const source of sources) {
|
|
78252
|
+
let parsed;
|
|
78253
|
+
try {
|
|
78254
|
+
parsed = JSON.parse(source);
|
|
78255
|
+
} catch (err) {
|
|
78256
|
+
failedEntries.push({
|
|
78257
|
+
entry: source,
|
|
78258
|
+
reason: `Not valid JSON (${err instanceof Error ? err.message : String(err)}). Expected {"name":"…","url":"https://…","token":"…"}.`
|
|
78259
|
+
});
|
|
78260
|
+
continue;
|
|
78261
|
+
}
|
|
78262
|
+
for (const candidate of Array.isArray(parsed) ? parsed : [parsed]) {
|
|
78263
|
+
const result = validateExternalMcpEntry(candidate, usedNames);
|
|
78264
|
+
if ("reason" in result) {
|
|
78265
|
+
failedEntries.push({
|
|
78266
|
+
entry: typeof candidate === "object" ? JSON.stringify(candidate) : String(candidate),
|
|
78267
|
+
reason: result.reason
|
|
78268
|
+
});
|
|
78269
|
+
continue;
|
|
78270
|
+
}
|
|
78271
|
+
usedNames.add(result.config.name);
|
|
78272
|
+
servers.push(result.config);
|
|
78273
|
+
}
|
|
78274
|
+
}
|
|
78275
|
+
return {
|
|
78276
|
+
servers,
|
|
78277
|
+
failedEntries
|
|
78278
|
+
};
|
|
78279
|
+
}
|
|
78280
|
+
/**
|
|
78281
|
+
* Validate one entry, exported so the resolve route can answer with the exact
|
|
78282
|
+
* verdict the header path would reach — a route that green-lights a server the
|
|
78283
|
+
* header then rejects is worse than no route at all.
|
|
78284
|
+
* @param candidate Parsed JSON entry.
|
|
78285
|
+
* @param usedNames Namespaces already taken by earlier entries in the same batch.
|
|
78286
|
+
*/
|
|
78287
|
+
function validateExternalMcpEntry(candidate, usedNames) {
|
|
78288
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) return { reason: "Entry must be a JSON object with \"name\" and \"url\"." };
|
|
78289
|
+
const entry = candidate;
|
|
78290
|
+
if (typeof entry.name !== "string" || entry.name === "") return { reason: "\"name\" is required and must be a non-empty string — it becomes the sandbox namespace." };
|
|
78291
|
+
if (!NAMESPACE_PATTERN.test(entry.name)) return { reason: `"name" must be a valid JavaScript identifier (letters, digits, underscore; not starting with a digit), got "${entry.name}".` };
|
|
78292
|
+
if (RESERVED_NAMESPACES.has(entry.name)) return { reason: `"name" must not be a reserved namespace (${[...RESERVED_NAMESPACES].join(", ")}), got "${entry.name}".` };
|
|
78293
|
+
if (usedNames.has(entry.name)) return { reason: `Duplicate namespace "${entry.name}" — each external MCP server needs its own name.` };
|
|
78294
|
+
if (typeof entry.url !== "string" || entry.url === "") return { reason: `"url" is required and must be a non-empty string (server "${entry.name}").` };
|
|
78295
|
+
let url;
|
|
78296
|
+
try {
|
|
78297
|
+
url = new URL(entry.url);
|
|
78298
|
+
} catch {
|
|
78299
|
+
return { reason: `"url" must be an absolute URL, got "${entry.url}" (server "${entry.name}").` };
|
|
78300
|
+
}
|
|
78301
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return { reason: `"url" must use http or https, got "${url.protocol}" (server "${entry.name}").` };
|
|
78302
|
+
if (entry.token !== void 0 && (typeof entry.token !== "string" || entry.token === "")) return { reason: `"token" must be a non-empty string when present (server "${entry.name}").` };
|
|
78303
|
+
if (entry.description !== void 0 && typeof entry.description !== "string") return { reason: `"description" must be a string when present (server "${entry.name}").` };
|
|
78304
|
+
let headers;
|
|
78305
|
+
if (entry.headers !== void 0) {
|
|
78306
|
+
if (typeof entry.headers !== "object" || entry.headers === null || Array.isArray(entry.headers)) return { reason: `"headers" must be an object of string values when present (server "${entry.name}").` };
|
|
78307
|
+
headers = {};
|
|
78308
|
+
for (const [key, value] of Object.entries(entry.headers)) {
|
|
78309
|
+
if (typeof value !== "string") return { reason: `"headers.${key}" must be a string (server "${entry.name}").` };
|
|
78310
|
+
headers[key] = value;
|
|
78311
|
+
}
|
|
78312
|
+
}
|
|
78313
|
+
return { config: {
|
|
78314
|
+
name: entry.name,
|
|
78315
|
+
url: entry.url,
|
|
78316
|
+
...typeof entry.token === "string" ? { token: entry.token } : {},
|
|
78317
|
+
...headers ? { headers } : {},
|
|
78318
|
+
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
78319
|
+
} };
|
|
78320
|
+
}
|
|
78321
|
+
/**
|
|
78322
|
+
* Request headers for one external server: the `token` bearer shorthand,
|
|
78323
|
+
* then the explicit `headers` map so it can override.
|
|
78324
|
+
* @param config External server config.
|
|
78325
|
+
*/
|
|
78326
|
+
function externalMcpHeaders(config) {
|
|
78327
|
+
return {
|
|
78328
|
+
...config.token ? { authorization: `Bearer ${config.token}` } : {},
|
|
78329
|
+
...config.headers
|
|
78330
|
+
};
|
|
78331
|
+
}
|
|
78332
|
+
/**
|
|
78333
|
+
* Transport for an external MCP server: plain `fetch` against the absolute
|
|
78334
|
+
* URL with the entry's own credentials attached. No tenant auth, no safeFetch
|
|
78335
|
+
* host pinning — the target is an arbitrary operator-chosen host, not the
|
|
78336
|
+
* tenant.
|
|
78337
|
+
* @param config External server config.
|
|
78338
|
+
*/
|
|
78339
|
+
function createExternalMcpFetch(config) {
|
|
78340
|
+
const authHeaders = externalMcpHeaders(config);
|
|
78341
|
+
return (url, init) => fetch(url, {
|
|
78342
|
+
...init,
|
|
78343
|
+
headers: {
|
|
78344
|
+
...init.headers,
|
|
78345
|
+
...authHeaders
|
|
78346
|
+
}
|
|
78347
|
+
});
|
|
78348
|
+
}
|
|
78349
|
+
//#endregion
|
|
78350
|
+
//#region src/codemode/external-mcp-session.ts
|
|
78351
|
+
const IDLE_TTL_MS = 900 * 1e3;
|
|
78352
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
78353
|
+
function configKey(config) {
|
|
78354
|
+
return JSON.stringify([
|
|
78355
|
+
config.name,
|
|
78356
|
+
config.url,
|
|
78357
|
+
config.token ?? "",
|
|
78358
|
+
config.headers ?? {}
|
|
78359
|
+
]);
|
|
78360
|
+
}
|
|
78361
|
+
function armIdleTimer(sessionKey) {
|
|
78362
|
+
const session = sessions.get(sessionKey);
|
|
78363
|
+
if (!session) return;
|
|
78364
|
+
if (session.timer) clearTimeout(session.timer);
|
|
78365
|
+
session.timer = setTimeout(() => evictExternalMcpSession(sessionKey, "idle-timeout"), IDLE_TTL_MS);
|
|
78366
|
+
session.timer.unref?.();
|
|
78367
|
+
}
|
|
78368
|
+
/**
|
|
78369
|
+
* Handshake one external server and read its tool list, bypassing the session
|
|
78370
|
+
* cache entirely.
|
|
78371
|
+
*
|
|
78372
|
+
* Exported for configuration checks (`POST /resolve-mcp-servers`): an operator
|
|
78373
|
+
* asking "does this URL and token work" must not be answered from a tool list
|
|
78374
|
+
* cached up to 15 minutes ago, and a candidate that may never be saved must not
|
|
78375
|
+
* seed the cache either. Runtime resolution goes through
|
|
78376
|
+
* {@link resolveExternalMcpServers}, which is where caching belongs.
|
|
78377
|
+
* @param config One external server config.
|
|
78378
|
+
*/
|
|
78379
|
+
async function probeExternalMcpServer(config) {
|
|
78380
|
+
const client = new McpHttpClient({
|
|
78381
|
+
url: config.url,
|
|
78382
|
+
fetch: createExternalMcpFetch(config)
|
|
78383
|
+
});
|
|
78384
|
+
try {
|
|
78385
|
+
const info = await client.initialize();
|
|
78386
|
+
const tools = await client.listTools();
|
|
78387
|
+
consola.info(`[external-mcp] "${config.name}" at ${config.url}: ${tools.length} tool(s)`);
|
|
78388
|
+
return {
|
|
78389
|
+
config,
|
|
78390
|
+
tools,
|
|
78391
|
+
instructions: info.instructions,
|
|
78392
|
+
serverName: info.serverName,
|
|
78393
|
+
serverVersion: info.serverVersion
|
|
78394
|
+
};
|
|
78395
|
+
} finally {
|
|
78396
|
+
await client.close();
|
|
78397
|
+
}
|
|
78398
|
+
}
|
|
78399
|
+
/**
|
|
78400
|
+
* Resolve every configured external MCP server for a session, using the cached
|
|
78401
|
+
* tool list where one exists. Servers whose handshake fails are reported in
|
|
78402
|
+
* `failures` and get no namespace — mirroring how a discovered service with a
|
|
78403
|
+
* failed spec download is skipped, except that these were explicitly requested
|
|
78404
|
+
* so the agent is told about them.
|
|
78405
|
+
* @param sessionKey MCP session id, or {@link CLI_EXTERNAL_MCP_SESSION} in CLI mode.
|
|
78406
|
+
* @param configs Parsed connection config.
|
|
78407
|
+
*/
|
|
78408
|
+
async function resolveExternalMcpServers(sessionKey, configs) {
|
|
78409
|
+
if (configs.length === 0) return {
|
|
78410
|
+
servers: [],
|
|
78411
|
+
failures: []
|
|
78412
|
+
};
|
|
78413
|
+
let session = sessions.get(sessionKey);
|
|
78414
|
+
if (!session) {
|
|
78415
|
+
session = { servers: /* @__PURE__ */ new Map() };
|
|
78416
|
+
sessions.set(sessionKey, session);
|
|
78417
|
+
}
|
|
78418
|
+
armIdleTimer(sessionKey);
|
|
78419
|
+
const settled = await Promise.all(configs.map(async (config) => {
|
|
78420
|
+
const key = configKey(config);
|
|
78421
|
+
let pending = session.servers.get(key);
|
|
78422
|
+
if (!pending) {
|
|
78423
|
+
pending = probeExternalMcpServer(config);
|
|
78424
|
+
session.servers.set(key, pending);
|
|
78425
|
+
pending.catch(() => {
|
|
78426
|
+
if (session.servers.get(key) === pending) session.servers.delete(key);
|
|
78427
|
+
});
|
|
78428
|
+
}
|
|
78429
|
+
try {
|
|
78430
|
+
return {
|
|
78431
|
+
ok: true,
|
|
78432
|
+
server: await pending
|
|
78433
|
+
};
|
|
78434
|
+
} catch (err) {
|
|
78435
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
78436
|
+
consola.warn(`[external-mcp] "${config.name}" at ${config.url} unavailable:`, reason);
|
|
78437
|
+
return {
|
|
78438
|
+
ok: false,
|
|
78439
|
+
failure: {
|
|
78440
|
+
name: config.name,
|
|
78441
|
+
url: config.url,
|
|
78442
|
+
reason
|
|
78443
|
+
}
|
|
78444
|
+
};
|
|
78445
|
+
}
|
|
78446
|
+
}));
|
|
78447
|
+
return {
|
|
78448
|
+
servers: settled.flatMap((r) => r.ok ? [r.server] : []),
|
|
78449
|
+
failures: settled.flatMap((r) => r.ok ? [] : [r.failure])
|
|
78450
|
+
};
|
|
78451
|
+
}
|
|
78452
|
+
/**
|
|
78453
|
+
* Drop a session's cached external tool lists and its timer.
|
|
78454
|
+
* @param sessionKey MCP session id (or the CLI key).
|
|
78455
|
+
* @param reason Why the entry is being dropped; included in the log line.
|
|
78456
|
+
*/
|
|
78457
|
+
function evictExternalMcpSession(sessionKey, reason) {
|
|
78458
|
+
const session = sessions.get(sessionKey);
|
|
78459
|
+
if (!session) return;
|
|
78460
|
+
if (session.timer) clearTimeout(session.timer);
|
|
78461
|
+
sessions.delete(sessionKey);
|
|
78462
|
+
consola.info(`[external-mcp] dropped cached tool lists for session ${sessionKey} (reason: ${reason})`);
|
|
78463
|
+
}
|
|
78464
|
+
/**
|
|
78465
|
+
* Evict every session (process exit, test cleanup).
|
|
78466
|
+
*/
|
|
78467
|
+
function disposeAllExternalMcpSessions() {
|
|
78468
|
+
for (const sessionKey of [...sessions.keys()]) evictExternalMcpSession(sessionKey, "shutdown");
|
|
78469
|
+
}
|
|
78470
|
+
//#endregion
|
|
77906
78471
|
//#region node_modules/.pnpm/tslib@2.8.1/node_modules/tslib/tslib.es6.mjs
|
|
77907
78472
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
77908
78473
|
function adopt(value) {
|
|
@@ -134718,177 +135283,6 @@ function createC8yAuthHeaders(auth) {
|
|
|
134718
135283
|
throw new Error("Invalid authentication credentials");
|
|
134719
135284
|
}
|
|
134720
135285
|
//#endregion
|
|
134721
|
-
//#region src/utils/mcp-client.ts
|
|
134722
|
-
const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
134723
|
-
const REQUEST_TIMEOUT_MS = 3e4;
|
|
134724
|
-
var McpHttpClient = class {
|
|
134725
|
-
#url;
|
|
134726
|
-
#fetch;
|
|
134727
|
-
#timeoutMs;
|
|
134728
|
-
#sessionId;
|
|
134729
|
-
#nextId = 1;
|
|
134730
|
-
#initialized;
|
|
134731
|
-
constructor(options) {
|
|
134732
|
-
this.#url = options.url;
|
|
134733
|
-
this.#fetch = options.fetch;
|
|
134734
|
-
this.#timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
134735
|
-
}
|
|
134736
|
-
/**
|
|
134737
|
-
* Initialize the session (idempotent — concurrent callers share one
|
|
134738
|
-
* handshake). Advertises no client capabilities: no elicitation, no
|
|
134739
|
-
* sampling, no roots.
|
|
134740
|
-
*/
|
|
134741
|
-
initialize() {
|
|
134742
|
-
this.#initialized ??= this.#doInitialize();
|
|
134743
|
-
return this.#initialized;
|
|
134744
|
-
}
|
|
134745
|
-
async #doInitialize() {
|
|
134746
|
-
const result = await this.#request("initialize", {
|
|
134747
|
-
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
134748
|
-
capabilities: {},
|
|
134749
|
-
clientInfo: {
|
|
134750
|
-
name: "mc8yp",
|
|
134751
|
-
version: "0.0.0"
|
|
134752
|
-
}
|
|
134753
|
-
});
|
|
134754
|
-
await this.#notify("notifications/initialized");
|
|
134755
|
-
return {
|
|
134756
|
-
serverName: result?.serverInfo?.name,
|
|
134757
|
-
serverVersion: result?.serverInfo?.version,
|
|
134758
|
-
instructions: result?.instructions
|
|
134759
|
-
};
|
|
134760
|
-
}
|
|
134761
|
-
/**
|
|
134762
|
-
* List every tool, following pagination cursors.
|
|
134763
|
-
*/
|
|
134764
|
-
async listTools() {
|
|
134765
|
-
await this.initialize();
|
|
134766
|
-
const tools = [];
|
|
134767
|
-
let cursor;
|
|
134768
|
-
do {
|
|
134769
|
-
const result = await this.#request("tools/list", cursor ? { cursor } : {});
|
|
134770
|
-
tools.push(...result?.tools ?? []);
|
|
134771
|
-
cursor = result?.nextCursor;
|
|
134772
|
-
} while (cursor);
|
|
134773
|
-
return tools;
|
|
134774
|
-
}
|
|
134775
|
-
/**
|
|
134776
|
-
* Call a tool and unwrap the result: structured content when present,
|
|
134777
|
-
* otherwise joined text content (JSON-parsed when possible). `isError`
|
|
134778
|
-
* results throw with the server's message.
|
|
134779
|
-
* @param name
|
|
134780
|
-
* @param args
|
|
134781
|
-
*/
|
|
134782
|
-
async callTool(name, args) {
|
|
134783
|
-
await this.initialize();
|
|
134784
|
-
const result = await this.#request("tools/call", {
|
|
134785
|
-
name,
|
|
134786
|
-
arguments: args && typeof args === "object" ? args : {}
|
|
134787
|
-
});
|
|
134788
|
-
if (!result || typeof result !== "object") return result;
|
|
134789
|
-
if (result.isError) {
|
|
134790
|
-
const message = (result.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("\n") || `MCP tool "${name}" failed`;
|
|
134791
|
-
throw new Error(message);
|
|
134792
|
-
}
|
|
134793
|
-
if (result.structuredContent != null) return result.structuredContent;
|
|
134794
|
-
const content = result.content ?? [];
|
|
134795
|
-
if (content.length === 0 || !content.every((c) => c.type === "text")) return result;
|
|
134796
|
-
const text = content.map((c) => c.text ?? "").join("\n");
|
|
134797
|
-
try {
|
|
134798
|
-
return JSON.parse(text);
|
|
134799
|
-
} catch {
|
|
134800
|
-
return text;
|
|
134801
|
-
}
|
|
134802
|
-
}
|
|
134803
|
-
/**
|
|
134804
|
-
* Best-effort session teardown. Never throws.
|
|
134805
|
-
*/
|
|
134806
|
-
async close() {
|
|
134807
|
-
if (!this.#sessionId) return;
|
|
134808
|
-
try {
|
|
134809
|
-
await this.#fetch(this.#url, {
|
|
134810
|
-
method: "DELETE",
|
|
134811
|
-
headers: { "mcp-session-id": this.#sessionId }
|
|
134812
|
-
});
|
|
134813
|
-
} catch {}
|
|
134814
|
-
this.#sessionId = void 0;
|
|
134815
|
-
this.#initialized = void 0;
|
|
134816
|
-
}
|
|
134817
|
-
async #notify(method) {
|
|
134818
|
-
await this.#post({
|
|
134819
|
-
jsonrpc: "2.0",
|
|
134820
|
-
method
|
|
134821
|
-
});
|
|
134822
|
-
}
|
|
134823
|
-
async #request(method, params) {
|
|
134824
|
-
const id = this.#nextId++;
|
|
134825
|
-
const response = await this.#post({
|
|
134826
|
-
jsonrpc: "2.0",
|
|
134827
|
-
id,
|
|
134828
|
-
method,
|
|
134829
|
-
params
|
|
134830
|
-
});
|
|
134831
|
-
const message = await this.#readResponse(response, id);
|
|
134832
|
-
if (message.error) throw new Error(`MCP ${method} failed: ${message.error.message}`);
|
|
134833
|
-
return message.result;
|
|
134834
|
-
}
|
|
134835
|
-
async #post(payload) {
|
|
134836
|
-
const controller = new AbortController();
|
|
134837
|
-
const timer = setTimeout(() => controller.abort(), this.#timeoutMs);
|
|
134838
|
-
try {
|
|
134839
|
-
const response = await this.#fetch(this.#url, {
|
|
134840
|
-
method: "POST",
|
|
134841
|
-
headers: {
|
|
134842
|
-
"content-type": "application/json",
|
|
134843
|
-
"accept": "application/json, text/event-stream",
|
|
134844
|
-
...this.#sessionId ? { "mcp-session-id": this.#sessionId } : {}
|
|
134845
|
-
},
|
|
134846
|
-
body: JSON.stringify(payload),
|
|
134847
|
-
signal: controller.signal
|
|
134848
|
-
});
|
|
134849
|
-
this.#sessionId ??= response.headers.get("mcp-session-id") ?? void 0;
|
|
134850
|
-
if (!response.ok && response.status !== 202) throw new Error(`MCP endpoint responded with ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`);
|
|
134851
|
-
return response;
|
|
134852
|
-
} finally {
|
|
134853
|
-
clearTimeout(timer);
|
|
134854
|
-
}
|
|
134855
|
-
}
|
|
134856
|
-
/**
|
|
134857
|
-
* Read the JSON-RPC response for `id` from a plain-JSON or SSE-framed
|
|
134858
|
-
* response body. Server→client requests encountered on the stream are
|
|
134859
|
-
* declined immediately (fire-and-forget error response) — mc8yp does not
|
|
134860
|
-
* forward elicitation or sampling.
|
|
134861
|
-
* @param response
|
|
134862
|
-
* @param id
|
|
134863
|
-
*/
|
|
134864
|
-
async #readResponse(response, id) {
|
|
134865
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
134866
|
-
const text = await response.text();
|
|
134867
|
-
const messages = contentType.includes("text/event-stream") ? text.split(/\n\n/).flatMap((event) => event.split("\n").filter((line) => line.startsWith("data: ")).map((line) => line.slice(6))).filter(Boolean).map((data) => JSON.parse(data)) : text.trim() ? [JSON.parse(text)] : [];
|
|
134868
|
-
for (const message of messages) {
|
|
134869
|
-
if (message.method && message.id !== void 0) {
|
|
134870
|
-
this.#declineServerRequest(message).catch(() => void 0);
|
|
134871
|
-
continue;
|
|
134872
|
-
}
|
|
134873
|
-
if (message.id === id) return message;
|
|
134874
|
-
}
|
|
134875
|
-
throw new Error(`MCP endpoint returned no response for request ${id}`);
|
|
134876
|
-
}
|
|
134877
|
-
async #declineServerRequest(request) {
|
|
134878
|
-
consola.warn(`[mcp-client] declining server-initiated request "${request.method}" — mc8yp does not forward elicitation or sampling.`);
|
|
134879
|
-
try {
|
|
134880
|
-
await this.#post({
|
|
134881
|
-
jsonrpc: "2.0",
|
|
134882
|
-
id: request.id,
|
|
134883
|
-
error: {
|
|
134884
|
-
code: -32601,
|
|
134885
|
-
message: `mc8yp does not forward ${request.method === "elicitation/create" ? "elicitation" : request.method === "sampling/createMessage" ? "sampling" : "server-initiated"} requests. The tool cannot interact with the user through this connection.`
|
|
134886
|
-
}
|
|
134887
|
-
});
|
|
134888
|
-
} catch {}
|
|
134889
|
-
}
|
|
134890
|
-
};
|
|
134891
|
-
//#endregion
|
|
134892
135286
|
//#region src/codemode/execute.ts
|
|
134893
135287
|
const EXECUTE_ENTRY_PATH = "/codemode-execute.mjs";
|
|
134894
135288
|
const BLOCKED_REQUEST_PREFIX = "Request blocked by MCP connection policy.";
|
|
@@ -134905,6 +135299,7 @@ async function getSandbox() {
|
|
|
134905
135299
|
}
|
|
134906
135300
|
process$1.once("exit", () => {
|
|
134907
135301
|
disposeAllSandboxSessions();
|
|
135302
|
+
disposeAllExternalMcpSessions();
|
|
134908
135303
|
if (sandboxPromise) sandboxPromise.then((s) => s.dispose()).catch(() => void 0);
|
|
134909
135304
|
});
|
|
134910
135305
|
function formatRestrictionBlockMessage(method, pathname, matching) {
|
|
@@ -135030,29 +135425,39 @@ function buildAgentModule(functionCode) {
|
|
|
135030
135425
|
"export default __mc8ypExecute"
|
|
135031
135426
|
].join("\n");
|
|
135032
135427
|
}
|
|
135033
|
-
function createLiveCalls(
|
|
135428
|
+
function createLiveCalls(tenant) {
|
|
135034
135429
|
const mcpClients = /* @__PURE__ */ new Map();
|
|
135035
|
-
const base = tenantUrl.endsWith("/") ? tenantUrl : `${tenantUrl}/`;
|
|
135036
135430
|
const mcpClientFor = (namespace) => {
|
|
135037
135431
|
let client = mcpClients.get(namespace.name);
|
|
135038
135432
|
if (!client) {
|
|
135039
|
-
client = new McpHttpClient({
|
|
135040
|
-
url: namespace.
|
|
135041
|
-
fetch: (
|
|
135042
|
-
...init,
|
|
135043
|
-
headers: {
|
|
135044
|
-
...init.headers,
|
|
135045
|
-
...namespace.server.sendAuthentication ? authHeaders : {}
|
|
135046
|
-
}
|
|
135047
|
-
})
|
|
135433
|
+
if (namespace.external) client = new McpHttpClient({
|
|
135434
|
+
url: namespace.external.url,
|
|
135435
|
+
fetch: createExternalMcpFetch(namespace.external)
|
|
135048
135436
|
});
|
|
135437
|
+
else {
|
|
135438
|
+
if ("error" in tenant) throw new Error(tenant.error);
|
|
135439
|
+
const { tenantUrl, authHeaders } = tenant;
|
|
135440
|
+
const base = tenantUrl.endsWith("/") ? tenantUrl : `${tenantUrl}/`;
|
|
135441
|
+
client = new McpHttpClient({
|
|
135442
|
+
url: namespace.server.url,
|
|
135443
|
+
fetch: (path, init) => fetch(new URL(path.replace(/^\//, ""), base), {
|
|
135444
|
+
...init,
|
|
135445
|
+
headers: {
|
|
135446
|
+
...init.headers,
|
|
135447
|
+
...namespace.server.sendAuthentication ? authHeaders : {}
|
|
135448
|
+
}
|
|
135449
|
+
})
|
|
135450
|
+
});
|
|
135451
|
+
}
|
|
135049
135452
|
mcpClients.set(namespace.name, client);
|
|
135050
135453
|
}
|
|
135051
135454
|
return client;
|
|
135052
135455
|
};
|
|
135053
135456
|
return {
|
|
135054
135457
|
operation: async (namespace, opName, input) => {
|
|
135055
|
-
|
|
135458
|
+
if ("error" in tenant) throw new Error(tenant.error);
|
|
135459
|
+
const op = namespace.operations.find((o) => o.name === opName);
|
|
135460
|
+
return performRequest(tenant.safeFetch, tenant.tenantUrl, toRequest(op, input));
|
|
135056
135461
|
},
|
|
135057
135462
|
mcpCall: async (namespace, toolName, args) => {
|
|
135058
135463
|
return mcpClientFor(namespace).callTool(toolName, args);
|
|
@@ -135063,17 +135468,7 @@ function createLiveCalls(safeFetch, tenantUrl, authHeaders) {
|
|
|
135063
135468
|
}
|
|
135064
135469
|
};
|
|
135065
135470
|
}
|
|
135066
|
-
function
|
|
135067
|
-
const fail = async () => {
|
|
135068
|
-
throw new Error(message);
|
|
135069
|
-
};
|
|
135070
|
-
return {
|
|
135071
|
-
operation: fail,
|
|
135072
|
-
mcpCall: fail,
|
|
135073
|
-
dispose: async () => {}
|
|
135074
|
-
};
|
|
135075
|
-
}
|
|
135076
|
-
function buildApiModule(namespaces, methodIndex, docsIndex, live, sandbox) {
|
|
135471
|
+
function buildApiModule(namespaces, methodIndex, docsIndex, live, sandbox, externalFailures) {
|
|
135077
135472
|
const visibleTargets = new Set(namespaces.flatMap((ns) => ns.kind === "openapi" ? ns.operations.map((op) => `${ns.name}.${op.name}`) : ns.tools.map((tool) => `${ns.name}.${tool.name}`)));
|
|
135078
135473
|
const sandboxEnabled = sandbox !== void 0;
|
|
135079
135474
|
return {
|
|
@@ -135090,9 +135485,15 @@ function buildApiModule(namespaces, methodIndex, docsIndex, live, sandbox) {
|
|
|
135090
135485
|
const targets = target.filter((t) => typeof t === "string" && t.trim() !== "");
|
|
135091
135486
|
if (targets.length === 0) throw new TypeError("codemode.describe(targets): pass method targets like \"c8y.getAlarmCollectionResource\"");
|
|
135092
135487
|
if (targets.length > 5) throw new TypeError(`codemode.describe(targets): at most 5 targets per call (got ${targets.length}) — shortlist candidates via search first`);
|
|
135093
|
-
return targets.map((t) => describeTarget(namespaces, methodIndex, t,
|
|
135488
|
+
return targets.map((t) => describeTarget(namespaces, methodIndex, t, {
|
|
135489
|
+
sandboxEnabled,
|
|
135490
|
+
externalFailures
|
|
135491
|
+
}));
|
|
135094
135492
|
}
|
|
135095
|
-
return describeTarget(namespaces, methodIndex, target == null ? void 0 : String(target),
|
|
135493
|
+
return describeTarget(namespaces, methodIndex, target == null ? void 0 : String(target), {
|
|
135494
|
+
sandboxEnabled,
|
|
135495
|
+
externalFailures
|
|
135496
|
+
});
|
|
135096
135497
|
}
|
|
135097
135498
|
},
|
|
135098
135499
|
docs: {
|
|
@@ -135110,18 +135511,28 @@ function buildApiModule(namespaces, methodIndex, docsIndex, live, sandbox) {
|
|
|
135110
135511
|
namespaces: Object.fromEntries(namespaces.map((namespace) => [namespace.name, namespace.kind === "openapi" ? Object.fromEntries(namespace.operations.map((op) => [op.name, async (...args) => live.operation(namespace, op.name, args[0])])) : Object.fromEntries(namespace.tools.map((tool) => [tool.name, async (...args) => live.mcpCall(namespace, tool.toolName, args[0])]))]))
|
|
135111
135512
|
};
|
|
135112
135513
|
}
|
|
135113
|
-
function resolveRuntime() {
|
|
135514
|
+
async function resolveRuntime() {
|
|
135114
135515
|
const custom = c8yMcpServer.ctx.custom;
|
|
135115
135516
|
const resolved = custom?.specs;
|
|
135116
135517
|
if (!resolved) throw new Error(custom?.env === "cli" ? "No active tenant set. Call set-active-tenant first." : "No tenant specs available for this MCP connection. This usually means the request reached the server without a resolvable tenant context (e.g. a platform probe). Reconnect with valid tenant auth.");
|
|
135117
135518
|
const restrictions = custom?.restrictions ?? [];
|
|
135118
135519
|
const allowRules = custom?.allowRules ?? [];
|
|
135119
135520
|
const noMcp = custom?.noMcp;
|
|
135521
|
+
const { servers: externalServers, failures: externalFailures } = await resolveExternalMcpServers(c8yMcpServer.ctx.sessionId ?? "cli", custom?.externalMcpServers ?? []);
|
|
135522
|
+
const namespaces = buildNamespaces(resolved, {
|
|
135523
|
+
restrictions,
|
|
135524
|
+
allowRules,
|
|
135525
|
+
noMcp,
|
|
135526
|
+
externalServers
|
|
135527
|
+
});
|
|
135528
|
+
const externalNames = new Set(namespaces.filter((ns) => ns.kind === "mcp" && ns.external).map((ns) => ns.name));
|
|
135120
135529
|
return {
|
|
135121
135530
|
resolved,
|
|
135122
135531
|
restrictions,
|
|
135123
135532
|
allowRules,
|
|
135124
|
-
namespaces
|
|
135533
|
+
namespaces,
|
|
135534
|
+
externalFailures,
|
|
135535
|
+
externalMethods: toSearchableMethods(namespaces.filter((ns) => externalNames.has(ns.name)))
|
|
135125
135536
|
};
|
|
135126
135537
|
}
|
|
135127
135538
|
const NO_DEFAULT_EXPORT_MESSAGE = "Execution completed without returning a value.";
|
|
@@ -135137,29 +135548,34 @@ function withCliTenantMarker(text, tenantUrl) {
|
|
|
135137
135548
|
return `${tenantUrl ? `Executed against tenant: ${tenantUrl}` : "No active tenant — discovery only. Live API calls require set-active-tenant, and visible specs are bundled reference snapshots that may not exist on any tenant."}\n\n${text}`;
|
|
135138
135549
|
}
|
|
135139
135550
|
async function execute(functionCode) {
|
|
135140
|
-
const { resolved, namespaces, restrictions, allowRules } = resolveRuntime();
|
|
135551
|
+
const { resolved, namespaces, restrictions, allowRules, externalFailures, externalMethods } = await resolveRuntime();
|
|
135141
135552
|
const SPEC_VIEW = {
|
|
135142
135553
|
all: true,
|
|
135143
135554
|
contextPaths: /* @__PURE__ */ new Set()
|
|
135144
135555
|
};
|
|
135145
|
-
const docsIndex = getDocsIndex(resolved, () => buildNamespaces(resolved,
|
|
135556
|
+
const docsIndex = getDocsIndex(resolved, () => buildNamespaces(resolved, { noMcp: SPEC_VIEW }).filter((ns) => ns.kind === "openapi").map((ns) => ({
|
|
135146
135557
|
namespace: ns.name,
|
|
135147
135558
|
spec: ns.spec
|
|
135148
135559
|
})));
|
|
135149
|
-
const
|
|
135560
|
+
const tenantMethodIndex = getMethodIndex(resolved, () => {
|
|
135150
135561
|
const byTarget = /* @__PURE__ */ new Map();
|
|
135151
|
-
for (const item of [...toSearchableMethods(buildNamespaces(resolved)), ...toSearchableMethods(buildNamespaces(resolved,
|
|
135562
|
+
for (const item of [...toSearchableMethods(buildNamespaces(resolved)), ...toSearchableMethods(buildNamespaces(resolved, { noMcp: SPEC_VIEW }))]) byTarget.set(item.target, item);
|
|
135152
135563
|
return [...byTarget.values()];
|
|
135153
135564
|
});
|
|
135565
|
+
const methodIndex = externalMethods.length === 0 ? tenantMethodIndex : buildMethodIndex([...tenantMethodIndex.methods.values(), ...externalMethods]);
|
|
135154
135566
|
let tenantUrl = null;
|
|
135155
135567
|
let live;
|
|
135156
135568
|
try {
|
|
135157
135569
|
const auth = await resolveC8yAuth();
|
|
135158
135570
|
tenantUrl = auth.tenantUrl;
|
|
135159
135571
|
const authHeaders = createC8yAuthHeaders(auth);
|
|
135160
|
-
live = createLiveCalls(
|
|
135572
|
+
live = createLiveCalls({
|
|
135573
|
+
safeFetch: createCumulocitySafeFetch(auth.tenantUrl, authHeaders, restrictions, allowRules),
|
|
135574
|
+
tenantUrl: auth.tenantUrl,
|
|
135575
|
+
authHeaders
|
|
135576
|
+
});
|
|
135161
135577
|
} catch (error) {
|
|
135162
|
-
live =
|
|
135578
|
+
live = createLiveCalls({ error: error instanceof Error ? error.message : String(error) });
|
|
135163
135579
|
}
|
|
135164
135580
|
const sessionId = c8yMcpServer.ctx.sessionId;
|
|
135165
135581
|
const sandboxApi = c8yMcpServer.ctx.custom?.env === "server" && sessionId && c8yMcpServer.ctx.custom?.enableSandbox ? buildSandboxApi(sessionId) : void 0;
|
|
@@ -135168,7 +135584,7 @@ async function execute(functionCode) {
|
|
|
135168
135584
|
filename: EXECUTE_ENTRY_PATH,
|
|
135169
135585
|
limits: SANDBOX_LIMITS,
|
|
135170
135586
|
imports: {
|
|
135171
|
-
[API_MODULE_SPECIFIER]: buildApiModule(namespaces, methodIndex, docsIndex, live, sandboxApi),
|
|
135587
|
+
[API_MODULE_SPECIFIER]: buildApiModule(namespaces, methodIndex, docsIndex, live, sandboxApi, externalFailures),
|
|
135172
135588
|
[AGENT_MODULE_SPECIFIER]: buildAgentModule(functionCode)
|
|
135173
135589
|
}
|
|
135174
135590
|
}).finally(() => live.dispose());
|
|
@@ -135255,10 +135671,11 @@ declare const docs: {
|
|
|
135255
135671
|
}
|
|
135256
135672
|
|
|
135257
135673
|
// API namespaces: \`c8y\` (Cumulocity core — always present) plus one global
|
|
135258
|
-
// per microservice available on the current tenant (e.g. \`dtm\`),
|
|
135259
|
-
//
|
|
135260
|
-
//
|
|
135261
|
-
//
|
|
135674
|
+
// per microservice available on the current tenant (e.g. \`dtm\`), plus any
|
|
135675
|
+
// external MCP server configured for this connection — each with one typed
|
|
135676
|
+
// method per operation. \`codemode.describe()\` lists what this connection
|
|
135677
|
+
// actually has. If a method seems missing, search with different wording; if
|
|
135678
|
+
// it truly does not exist, say so instead of improvising:
|
|
135262
135679
|
// await c8y.getManagedObjectCollectionResource({ pageSize: 5 })
|
|
135263
135680
|
\`\`\`
|
|
135264
135681
|
|
|
@@ -140737,17 +141154,17 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140737
141154
|
"openapi": "3.0.1",
|
|
140738
141155
|
"info": {
|
|
140739
141156
|
"title": "DTM Asset and Definition API",
|
|
140740
|
-
"description": "The Digital Twin Manager (DTM) enables schema-based modeling in Cumulocity.\nIt allows creating and managing data model schemas, which serve as blueprints for all Cumulocity domain model entities such as assets and their properties, but also, for example, events, alarms, and measurements.\nThese blueprints act as reusable templates within the platform and are used to describe the logical structure, hierarchies and constraints of
|
|
141157
|
+
"description": "The Digital Twin Manager (DTM) enables schema-based modeling in Cumulocity.\nIt allows creating and managing data model schemas, which serve as blueprints for all Cumulocity domain model entities such as assets and their properties, but also, for example, events, alarms, and measurements.\nThese blueprints act as reusable templates within the platform and are used to describe the logical structure, hierarchies and constraints of complex [business assets](https://cumulocity.com/docs/glossary/#assets) also known as [digital twins]\n(https://cumulocity.com/docs/glossary/#digital-twin).\n\nThe DTM API is the interface to manage the schema definitions as well as the (asset) instances based on these definitions. The API is structured to separate schema governance (Definition API) from instance management (Asset API):\n\n### Definition API\n\nThe **Definition API** provides the governance layer of the DTM to manage the reusable schema elements that define the structure used to describe, create or validate schema for domain model entities, called _definitions_.\nBy default, the definitions are maintained on each tenant individually, but they can also be shared across tenants in a Cumulocity multi-tenant environment. To achieve that, the tenant option `definitions.multitenant.sharing.mode` needs to be set\nto `enabled` on the enterprise tenant and the subtenants. When sharing is enabled, the definitions can be created and updated only in the enterprise tenant. The subtenants will have read-only access to the shared definitions.\n\n### Asset API\n\nThe **Asset API** allows managing asset instances based on predefined Asset Definitions (also known as [Asset Models](https://cumulocity.com/docs/glossary/#asset-models)). Asset instances created from an Asset Model inherit the structure and\nconstraints defined in the model.\n\n# Cumulocity REST API\n\nThe DTM Definition API and Asset API are an extension of the Cumulocity REST API and follow the same design principles and aspects common to all REST-based interfaces of Cumulocity. For general information about the Cumulocity REST API, see the\n[Cumulocity REST API documentation](https://cumulocity.com/api/core/).\n\n# Authorization\n\nAll requests issued to DTM Definition API and Asset API are subject to authentication and authorization. For detailed information about authentication, refer to the Cumulocity Core OpenAPI specification details on [Authentication]\n(https://cumulocity.com/api/core/#section/Authentication). To determine the required permissions, see the \"Required user role\" entries for the individual requests.\n\nFor general information about permissions and the concept of ownership in Cumulocity,\nsee [Getting started > Technical concepts > Security aspects > Access control > Managing roles and assigning permissions](https://www.cumulocity.com/docs/concepts/security/#managing-roles-and-assigning-permissions) in\nthe [Cumulocity user documentation](https://cumulocity.com/docs/).\n<br>\n# Accessing the DTM API\n**Info:** The URL paths of proxied requests consist of:\n* the path of the microservice, which you will find in the application properties of the microservice\n* the corresponding DTM REST API path.\n\n**Example:** For the system `eu-latest.cumulocity.com` and tenant domain name `dtm-demo`, the base URL would be `https://dtm-demo.eu-latest.cumulocity.com/service/dtm/`. The endpoint to query assets is `/assets`. Thus, the complete endpoint path is `https://dtm-demo.eu-latest.cumulocity.com/service/dtm/assets`.\n",
|
|
140741
141158
|
"version": "Latest"
|
|
140742
141159
|
},
|
|
140743
141160
|
"servers": [{
|
|
140744
|
-
"url": "
|
|
141161
|
+
"url": "/",
|
|
140745
141162
|
"description": "The Digital Twin Manager service."
|
|
140746
141163
|
}],
|
|
140747
141164
|
"tags": [
|
|
140748
141165
|
{
|
|
140749
141166
|
"name": "Assets",
|
|
140750
|
-
"description": "The Asset API extends the Cumulocity core capabilities and domain model to manage assets with schema-based governance by linking asset instances to predefined Asset Definitions (also known as Asset Models). The Asset Definition serves as a blueprint\nfor the asset instances, defining\ntheir structure and constraints and relationships within an asset hierarchy.\n\nAsset instances without a linked Asset Definition are treated as generic assets without schema governance or constraints of the logical structure defined in an Asset Definition.\n\nThe Asset API extends the Cumulocity core capabilities with\n* Schema Governance: Enforcing and managing the underlying data schemas.\n* Integration: Synchronizing external sources of assets and digital twins.\n* Bulk Operations: Facilitating large-scale asset management.\n* Linked Series: Linking of time-series context to assets.\n* Permissions: Elevated permissions for synchronization and linked series management.\n\n### Asset Synchronization\n\nThe Asset API provides optimized operations specifically designed to efficiently create or update assets within synchronization workflows from external systems.\n\nFor referencing an asset from an external system, a unique identifier is required to map the asset between Cumulocity and the external system. This unique identifier is used in addition to the Cumulocity asset id and can be used to query the asset via the Asset API. This has a significant performance benefit over using any custom fragment property or even properties to identify the asset. Internally the Asset API creates an external id in the Identity API of type `c8y_Asset` for each asset that has an external identifier set via the `c8y_ExternalId` fragment.\n\nUsing the external identifier, the Asset API can perform idempotent create or update operations. This means that if an asset with the specified external identifier already exists, it will be updated; otherwise, a new asset will be created. This is particularly useful in synchronization scenarios where the same asset data may be processed multiple times, ensuring that duplicate assets are not created.\n\nIf the asset has the `c8y_ExternalAsset` fragment, it is considered to be externally managed applying additional restrictions on update and delete operations to avoid unintended modifications of externally managed assets. See the Permissions section below for more details.\n\nThe `c8y_ExternalAsset` fragment allows defining a `source` to indicate the origin of the external asset.\n\n```json\n{\n \"c8y_ExternalAsset\": {\n \"source\": \"string\"\n }\n}\n```\n\n### Permissions\n\nBy default, for all Asset API operations, users require the corresponding *ROLE_INVENTORY_\\** permission to create, update or delete assets. The *ROLE_DIGITAL_TWIN_ASSETS_\\** permissions are elevated permissions that are specifically designed to control modifications of externally managed assets in addition to the *ROLE_INVENTORY_\\** roles and permissions.\n\
|
|
141167
|
+
"description": "The Asset API extends the Cumulocity core capabilities and domain model to manage assets with schema-based governance by linking asset instances to predefined Asset Definitions (also known as Asset Models). The Asset Definition serves as a blueprint\nfor the asset instances, defining\ntheir structure and constraints and relationships within an asset hierarchy.\n\nAsset instances without a linked Asset Definition are treated as generic assets without schema governance or constraints of the logical structure defined in an Asset Definition.\n\nThe Asset API extends the Cumulocity core capabilities with\n* Schema Governance: Enforcing and managing the underlying data schemas.\n* Integration: Synchronizing external sources of assets and digital twins.\n* Bulk Operations: Facilitating large-scale asset management.\n* Linked Series: Linking of time-series context to assets.\n* Permissions: Elevated permissions for synchronization and linked series management.\n\n### Asset Synchronization\n\nThe Asset API provides optimized operations specifically designed to efficiently create or update assets within synchronization workflows from external systems.\n\nFor referencing an asset from an external system, a unique identifier is required to map the asset between Cumulocity and the external system. This unique identifier is used in addition to the Cumulocity asset id and can be used to query the asset via the Asset API. This has a significant performance benefit over using any custom fragment property or even properties to identify the asset. Internally the Asset API creates an external id in the Identity API of type `c8y_Asset` for each asset that has an external identifier set via the `c8y_ExternalId` fragment.\n\nUsing the external identifier, the Asset API can perform idempotent create or update operations. This means that if an asset with the specified external identifier already exists, it will be updated; otherwise, a new asset will be created. This is particularly useful in synchronization scenarios where the same asset data may be processed multiple times, ensuring that duplicate assets are not created.\n\nIf the asset has the `c8y_ExternalAsset` fragment, it is considered to be externally managed applying additional restrictions on update and delete operations to avoid unintended modifications of externally managed assets. See the Permissions section below for more details.\n\nThe `c8y_ExternalAsset` fragment allows defining a `source` to indicate the origin of the external asset.\n\n```json\n{\n \"c8y_ExternalAsset\": {\n \"source\": \"string\"\n }\n}\n```\n\n### Permissions\n\nBy default, for all Asset API operations, users require the corresponding *ROLE_INVENTORY_\\** permission to create, update or delete assets. The *ROLE_DIGITAL_TWIN_ASSETS_\\** permissions are elevated permissions that are specifically designed to control modifications of externally managed assets in addition to the *ROLE_INVENTORY_\\** roles and permissions.\n\nBy default, the *ROLE_DIGITAL_TWIN_ASSETS_\\** permissions are only required for updating (including assigning devices) or deleting externally managed assets (assets having the `c8y_ExternalAsset` fragment).\nThis is especially useful for using the Asset API for synchronization of assets from an external source and to control modifications of these externally managed assets within Cumulocity while still allowing regular\n(not external) assets to be managed without additional permissions.\n\nThis behavior can be configured by setting the tenant option `assets.permission.mode`: to enforce **always** requiring the *ROLE_DIGITAL_TWIN_ASSETS_\\** permissions, no matter if the asset is external or not, the tenant option can be configured to\n`all`. In this case, the *ROLE_DIGITAL_TWIN_ASSETS_CREATE* permission is required to create assets. To disable the requirement of *ROLE_DIGITAL_TWIN_ASSETS_\\** permissions completely, the tenant option can be set to `none`.\n\nThe following elevated permissions are available for the Asset API:\n\n- *ROLE_DIGITAL_TWIN_ASSETS_CREATE*: Users can create new assets including LinkedSeries and their Source.\n- *ROLE_DIGITAL_TWIN_ASSETS_UPDATE*: Users can update existing assets including LinkedSeries and their Source.\n- *ROLE_DIGITAL_TWIN_ASSETS_ADMIN*: Users can manage all assets, including creating, updating, and deleting them.\n"
|
|
140751
141168
|
},
|
|
140752
141169
|
{
|
|
140753
141170
|
"name": "Linked Series",
|
|
@@ -140779,7 +141196,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140779
141196
|
"get": {
|
|
140780
141197
|
"tags": ["Property Definitions"],
|
|
140781
141198
|
"summary": "Retrieve a Property Definition by identifier",
|
|
140782
|
-
"description": "Finds a `Property Definition` by its `identifier`.\n\n
|
|
141199
|
+
"description": "Finds a `Property Definition` by its `identifier`.\n\n If `applicableTo` is specified, the `identifier` needs to point to an existing `Property Definition` that is applicable to that domain entity. If `applicableTo` is not specified, the operation will return the `Property Definition` that is **not applicable to any** domain entity (i.e. where no context is applied). If no such `Property Definition` exists, it will respond with `HTTP 404`. ",
|
|
140783
141200
|
"operationId": "getPropertyDefinition",
|
|
140784
141201
|
"parameters": [{
|
|
140785
141202
|
"name": "identifier",
|
|
@@ -140790,7 +141207,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140790
141207
|
}, {
|
|
140791
141208
|
"name": "applicableTo",
|
|
140792
141209
|
"in": "query",
|
|
140793
|
-
"description": "Limits the response to Property Definitions applicable to the specified domain entity.",
|
|
141210
|
+
"description": "Limits the response to the Property Definitions that are applicable to the specified domain entity.",
|
|
140794
141211
|
"schema": {
|
|
140795
141212
|
"type": "string",
|
|
140796
141213
|
"enum": [
|
|
@@ -140812,7 +141229,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140812
141229
|
"put": {
|
|
140813
141230
|
"tags": ["Property Definitions"],
|
|
140814
141231
|
"summary": "Update an existing Property Definition",
|
|
140815
|
-
"description": "Updates an existing `Property Definition`.\n\n Updates the `Property Definition` that is exactly identified by the `identifier` and `contexts` in the path. The `Property Definition` must be applicable to all the specified domain entities (and no others). If no contexts is specified, the `Property Definition` that is not applicable to any domain entity will be updated. The `requestBody` is used to update the `Property Definition` with the new values. This operation is restricted to updating the existing JSON Schema (including `title` and `description`), `contexts`, `tags`, and other custom fragments. Updating the `identifier` is not permitted.",
|
|
141232
|
+
"description": "Updates an existing `Property Definition`.\n\n Updates the `Property Definition` that is exactly identified by the `identifier` and `contexts` in the path. The `Property Definition` must be applicable to all the specified domain entities (and no others). If no contexts is specified, the `Property Definition` that is not applicable to any domain entity will be updated. The `requestBody` is used to update the `Property Definition` with the new values. This operation is restricted to updating the existing JSON Schema (including `title` and `description`), `contexts`, `tags`, and other custom fragments. Updating the `identifier` is not permitted. ",
|
|
140816
141233
|
"operationId": "updatePropertyDefinition",
|
|
140817
141234
|
"parameters": [{
|
|
140818
141235
|
"name": "identifier",
|
|
@@ -140823,7 +141240,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140823
141240
|
}, {
|
|
140824
141241
|
"name": "contexts",
|
|
140825
141242
|
"in": "query",
|
|
140826
|
-
"description": "
|
|
141243
|
+
"description": "Indicates the resource to filter for the Property Definition that matches exactly the specified domain entities.",
|
|
140827
141244
|
"explode": false,
|
|
140828
141245
|
"schema": {
|
|
140829
141246
|
"type": "array",
|
|
@@ -140854,7 +141271,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140854
141271
|
"delete": {
|
|
140855
141272
|
"tags": ["Property Definitions"],
|
|
140856
141273
|
"summary": "Delete an existing Property Definition",
|
|
140857
|
-
"description": "Deletes an existing `Property Definition` by its `identifier` and `contexts`.\n\n Deletes only the Property Definition that is exactly applicable to all the specified domain entities (and no others). If no contexts is specified, the operation will delete the Property Definition that is not applicable to any domain entity.",
|
|
141274
|
+
"description": "Deletes an existing `Property Definition` by its `identifier` and `contexts`.\n\n Deletes only the Property Definition that is exactly applicable to all the specified domain entities (and no others). If no contexts is specified, the operation will delete the Property Definition that is not applicable to any domain entity. ",
|
|
140858
141275
|
"operationId": "deletePropertyDefinition",
|
|
140859
141276
|
"parameters": [{
|
|
140860
141277
|
"name": "identifier",
|
|
@@ -140865,7 +141282,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140865
141282
|
}, {
|
|
140866
141283
|
"name": "contexts",
|
|
140867
141284
|
"in": "query",
|
|
140868
|
-
"description": "
|
|
141285
|
+
"description": "Indicates the resource to filter for the Property Definition that matches exactly the specified domain entities.",
|
|
140869
141286
|
"explode": false,
|
|
140870
141287
|
"schema": {
|
|
140871
141288
|
"type": "array",
|
|
@@ -140890,7 +141307,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140890
141307
|
"get": {
|
|
140891
141308
|
"tags": ["Measurement Definitions"],
|
|
140892
141309
|
"summary": "Retrieve Measurement Definitions",
|
|
140893
|
-
"description": "Finds a collection of `Measurement Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Measurement Definition`s.\n\n * Passing only one `title` results in `0...1` `Measurement Definition`s.\n\n\n\n\n\n The following rules will be applied when searching for the `Measurement Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
141310
|
+
"description": "Finds a collection of `Measurement Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Measurement Definition`s.\n\n * Passing only one `title` results in `0...1` `Measurement Definition`s.\n\n \n\n \n\n The following rules will be applied when searching for the `Measurement Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
140894
141311
|
"operationId": "getMeasurementDefinitions",
|
|
140895
141312
|
"parameters": [
|
|
140896
141313
|
{
|
|
@@ -140951,9 +141368,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140951
141368
|
"in": "query",
|
|
140952
141369
|
"description": "The current page number to be retrieved.",
|
|
140953
141370
|
"schema": {
|
|
140954
|
-
"
|
|
140955
|
-
"
|
|
140956
|
-
"default": 1
|
|
141371
|
+
"minimum": 1,
|
|
141372
|
+
"type": "integer"
|
|
140957
141373
|
}
|
|
140958
141374
|
},
|
|
140959
141375
|
{
|
|
@@ -140975,7 +141391,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140975
141391
|
"put": {
|
|
140976
141392
|
"tags": ["Measurement Definitions"],
|
|
140977
141393
|
"summary": "Update an existing Measurement Definition",
|
|
140978
|
-
"description": "Updates an existing `Measurement Definition`.\n\n The Measurement Definition's `identifier` is taken from the `requestBody`. This operation is restricted to updating the existing JSON Schema (including `title` and `description`). No other modifications are permitted.",
|
|
141394
|
+
"description": "Updates an existing `Measurement Definition`.\n\n The Measurement Definition's `identifier` is taken from the `requestBody`. This operation is restricted to updating the existing JSON Schema (including `title` and `description`). No other modifications are permitted. ",
|
|
140979
141395
|
"operationId": "updateMeasurementDefinition",
|
|
140980
141396
|
"requestBody": {
|
|
140981
141397
|
"description": "The data payload representing the `Measurement Definition` to be updated.",
|
|
@@ -140990,7 +141406,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140990
141406
|
"post": {
|
|
140991
141407
|
"tags": ["Measurement Definitions"],
|
|
140992
141408
|
"summary": "Create a new Measurement Definition",
|
|
140993
|
-
"description": "Creates a new `Measurement Definition`.\n\n This operation ensures consistency by enforcing the following rules: \n\n * The `identifier` of the `Measurement Definition` must be unique.\n\n * If a `title` is provided, it must also be unique.\n\n\n\n\n\n The supplied JSON Schema must conform to the [JSON Schema Draft-7 specification](http://json-schema.org/draft-07/schema).",
|
|
141409
|
+
"description": "Creates a new `Measurement Definition`.\n\n This operation ensures consistency by enforcing the following rules: \n\n * The `identifier` of the `Measurement Definition` must be unique.\n\n * If a `title` is provided, it must also be unique.\n\n \n\n\n\n The supplied JSON Schema must conform to the [JSON Schema Draft-7 specification](http://json-schema.org/draft-07/schema). ",
|
|
140994
141410
|
"operationId": "createMeasurementDefinition",
|
|
140995
141411
|
"requestBody": {
|
|
140996
141412
|
"description": "The data payload representing the `Measurement Definition` to be created.",
|
|
@@ -141007,7 +141423,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141007
141423
|
"get": {
|
|
141008
141424
|
"tags": ["Event Definitions"],
|
|
141009
141425
|
"summary": "Retrieve Event Definitions",
|
|
141010
|
-
"description": "Finds a collection of `Event Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Event Definition`s.\n\n * Passing only one `title` results in `0...1` `Event Definition`s.\n\n\n\n\n\n The following rules will be applied when searching for the `Event Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
141426
|
+
"description": "Finds a collection of `Event Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Event Definition`s.\n\n * Passing only one `title` results in `0...1` `Event Definition`s.\n\n \n\n \n\n The following rules will be applied when searching for the `Event Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
141011
141427
|
"operationId": "getEventDefinitions",
|
|
141012
141428
|
"parameters": [
|
|
141013
141429
|
{
|
|
@@ -141068,9 +141484,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141068
141484
|
"in": "query",
|
|
141069
141485
|
"description": "The current page number to be retrieved.",
|
|
141070
141486
|
"schema": {
|
|
141071
|
-
"
|
|
141072
|
-
"
|
|
141073
|
-
"default": 1
|
|
141487
|
+
"minimum": 1,
|
|
141488
|
+
"type": "integer"
|
|
141074
141489
|
}
|
|
141075
141490
|
},
|
|
141076
141491
|
{
|
|
@@ -141092,7 +141507,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141092
141507
|
"put": {
|
|
141093
141508
|
"tags": ["Event Definitions"],
|
|
141094
141509
|
"summary": "Update an existing Event Definition",
|
|
141095
|
-
"description": "Updates an existing `Event Definition`.\n\n The Event Definition's `identifier` is taken from the `requestBody`. This operation is restricted to updating the existing JSON Schema (including `title` and `description`). No other modifications are permitted.",
|
|
141510
|
+
"description": "Updates an existing `Event Definition`.\n\n The Event Definition's `identifier` is taken from the `requestBody`. This operation is restricted to updating the existing JSON Schema (including `title` and `description`). No other modifications are permitted. ",
|
|
141096
141511
|
"operationId": "updateEventDefinition",
|
|
141097
141512
|
"requestBody": {
|
|
141098
141513
|
"description": "The data payload representing the `Event Definition` to be updated.",
|
|
@@ -141107,7 +141522,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141107
141522
|
"post": {
|
|
141108
141523
|
"tags": ["Event Definitions"],
|
|
141109
141524
|
"summary": "Create a new Event Definition",
|
|
141110
|
-
"description": "Creates a new `Event Definition`.\n\n This operation ensures consistency by enforcing the following rules: \n\n * The `identifier` of the `Event Definition` must be unique.\n\n * If a `title` is provided, it must also be unique.\n\n\n\n\n\n The supplied JSON Schema must conform to the [JSON Schema Draft-7 specification](http://json-schema.org/draft-07/schema).",
|
|
141525
|
+
"description": "Creates a new `Event Definition`.\n\n This operation ensures consistency by enforcing the following rules: \n\n * The `identifier` of the `Event Definition` must be unique.\n\n * If a `title` is provided, it must also be unique.\n\n \n\n\n\n The supplied JSON Schema must conform to the [JSON Schema Draft-7 specification](http://json-schema.org/draft-07/schema). ",
|
|
141111
141526
|
"operationId": "createEventDefinition",
|
|
141112
141527
|
"requestBody": {
|
|
141113
141528
|
"description": "The data payload representing the `Event Definition` to be created.",
|
|
@@ -141124,7 +141539,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141124
141539
|
"get": {
|
|
141125
141540
|
"tags": ["Asset Definitions"],
|
|
141126
141541
|
"summary": "Retrieve Asset Definitions",
|
|
141127
|
-
"description": "Finds a collection of `Asset Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Asset Definition`s.\n\n * Passing only one `title` results in `0...1` `Asset Definition`s.\n\n\n\n\n\n The following rules will be applied when searching for the `Asset Definition`s: \n\n * `identifiers`, `titles`, `onlyRoots` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
141542
|
+
"description": "Finds a collection of `Asset Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Asset Definition`s.\n\n * Passing only one `title` results in `0...1` `Asset Definition`s.\n\n \n\n \n\n The following rules will be applied when searching for the `Asset Definition`s: \n\n * `identifiers`, `titles`, `onlyRoots` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
141128
141543
|
"operationId": "getAssetDefinitions",
|
|
141129
141544
|
"parameters": [
|
|
141130
141545
|
{
|
|
@@ -141194,9 +141609,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141194
141609
|
"in": "query",
|
|
141195
141610
|
"description": "The current page number to be retrieved.",
|
|
141196
141611
|
"schema": {
|
|
141197
|
-
"
|
|
141198
|
-
"
|
|
141199
|
-
"default": 1
|
|
141612
|
+
"minimum": 1,
|
|
141613
|
+
"type": "integer"
|
|
141200
141614
|
}
|
|
141201
141615
|
},
|
|
141202
141616
|
{
|
|
@@ -141218,7 +141632,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141218
141632
|
"put": {
|
|
141219
141633
|
"tags": ["Asset Definitions"],
|
|
141220
141634
|
"summary": "Update an existing Asset Definition",
|
|
141221
|
-
"description": "Updates the `Asset Definition`.\n\n Throws a `ConflictException` if validation fails due to conflicts in the specified properties or sub-assets. Such conflicts occur when one or more of the provided allowed properties or sub-assets either do not exist or are not applicable within the current `asset` context. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`*",
|
|
141635
|
+
"description": "Updates the `Asset Definition`.\n\n Throws a `ConflictException` if validation fails due to conflicts in the specified properties or sub-assets. Such conflicts occur when one or more of the provided allowed properties or sub-assets either do not exist or are not applicable within the current `asset` context. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`* ",
|
|
141222
141636
|
"operationId": "updateAssetDefinition",
|
|
141223
141637
|
"requestBody": {
|
|
141224
141638
|
"description": "The data payload containing only the fields that need to be updated or added to the existing `Asset Definition`. Fields\nnot included in the payload will remain unchanged.",
|
|
@@ -141250,7 +141664,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141250
141664
|
"get": {
|
|
141251
141665
|
"tags": ["Alarm Definitions"],
|
|
141252
141666
|
"summary": "Retrieve a collection of Alarm Definitions",
|
|
141253
|
-
"description": "Finds a collection of `Alarm Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Alarm Definition`s.\n\n * Passing only one `title` results in `0...1` `Alarm Definition`s.\n\n\n\n\n\n The following rules will be applied when searching for the `Alarm Definition`s: \n\n * `identifiers`, `titles` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
141667
|
+
"description": "Finds a collection of `Alarm Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Alarm Definition`s.\n\n * Passing only one `title` results in `0...1` `Alarm Definition`s.\n\n \n\n \n\n The following rules will be applied when searching for the `Alarm Definition`s: \n\n * `identifiers`, `titles` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
141254
141668
|
"operationId": "getAlarmDefinitions",
|
|
141255
141669
|
"parameters": [
|
|
141256
141670
|
{
|
|
@@ -141311,9 +141725,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141311
141725
|
"in": "query",
|
|
141312
141726
|
"description": "The current page number to be retrieved.",
|
|
141313
141727
|
"schema": {
|
|
141314
|
-
"
|
|
141315
|
-
"
|
|
141316
|
-
"default": 1
|
|
141728
|
+
"minimum": 1,
|
|
141729
|
+
"type": "integer"
|
|
141317
141730
|
}
|
|
141318
141731
|
},
|
|
141319
141732
|
{
|
|
@@ -141335,7 +141748,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141335
141748
|
"put": {
|
|
141336
141749
|
"tags": ["Alarm Definitions"],
|
|
141337
141750
|
"summary": "Update an existing Alarm Definition",
|
|
141338
|
-
"description": "Updates an existing `Alarm Definition`.\n\n The Alarm Definition's `identifier` is taken from the `requestBody`. This operation is restricted to updating the existing JSON Schema (including `title` and `description`). No other modifications are permitted.",
|
|
141751
|
+
"description": "Updates an existing `Alarm Definition`.\n\n The Alarm Definition's `identifier` is taken from the `requestBody`. This operation is restricted to updating the existing JSON Schema (including `title` and `description`). No other modifications are permitted. ",
|
|
141339
141752
|
"operationId": "updateAlarmDefinition",
|
|
141340
141753
|
"requestBody": {
|
|
141341
141754
|
"description": "The data payload representing the `Alarm Definition` to be updated.",
|
|
@@ -141350,7 +141763,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141350
141763
|
"post": {
|
|
141351
141764
|
"tags": ["Alarm Definitions"],
|
|
141352
141765
|
"summary": "Create a new Alarm Definition",
|
|
141353
|
-
"description": "Creates a new `Alarm Definition`.\n\n This operation ensures consistency by enforcing the following rules: \n\n * The `identifier` of the `Alarm Definition` must be unique.\n\n * If a `title` is provided, it must also be unique.\n\n\n\n\n\n The supplied JSON Schema must conform to the [JSON Schema Draft-7 specification](http://json-schema.org/draft-07/schema).",
|
|
141766
|
+
"description": "Creates a new `Alarm Definition`.\n\n This operation ensures consistency by enforcing the following rules: \n\n * The `identifier` of the `Alarm Definition` must be unique.\n\n * If a `title` is provided, it must also be unique.\n\n \n\n\n\n The supplied JSON Schema must conform to the [JSON Schema Draft-7 specification](http://json-schema.org/draft-07/schema). ",
|
|
141354
141767
|
"operationId": "createAlarmDefinition",
|
|
141355
141768
|
"requestBody": {
|
|
141356
141769
|
"description": "The data payload representing the `Alarm Definition` to be created.",
|
|
@@ -141388,7 +141801,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141388
141801
|
{
|
|
141389
141802
|
"name": "withSubAssets",
|
|
141390
141803
|
"in": "query",
|
|
141391
|
-
"description": "Indicates the resource to add the sub-assets in the response.",
|
|
141804
|
+
"description": "**Deprecated** – use the dedicated `/assets/{assetId}/subAssets`, `/assets/externalIds/{externalId}/subAssets` or `/assets/subAssets` endpoint instead!<br>Indicates the resource to add the sub-assets in the response.",
|
|
141805
|
+
"deprecated": true,
|
|
141392
141806
|
"schema": {
|
|
141393
141807
|
"type": "boolean",
|
|
141394
141808
|
"default": false
|
|
@@ -141402,6 +141816,24 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141402
141816
|
"type": "boolean",
|
|
141403
141817
|
"default": false
|
|
141404
141818
|
}
|
|
141819
|
+
},
|
|
141820
|
+
{
|
|
141821
|
+
"name": "withParents",
|
|
141822
|
+
"in": "query",
|
|
141823
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
141824
|
+
"schema": {
|
|
141825
|
+
"type": "boolean",
|
|
141826
|
+
"default": false
|
|
141827
|
+
}
|
|
141828
|
+
},
|
|
141829
|
+
{
|
|
141830
|
+
"name": "withChildrenCount",
|
|
141831
|
+
"in": "query",
|
|
141832
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
141833
|
+
"schema": {
|
|
141834
|
+
"type": "boolean",
|
|
141835
|
+
"default": false
|
|
141836
|
+
}
|
|
141405
141837
|
}
|
|
141406
141838
|
],
|
|
141407
141839
|
"responses": { "200": {
|
|
@@ -141412,7 +141844,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141412
141844
|
"put": {
|
|
141413
141845
|
"tags": ["Assets"],
|
|
141414
141846
|
"summary": "Update an existing asset",
|
|
141415
|
-
"description": "Updates an existing asset. All properties of the asset can be updated, including `name`, `type`,\n`c8y_ExternalId`, and fragments - except the `id`. Missing properties in the request body will not be removed but stay\nunchanged. Fragments, however, will be replaced completely, so missing properties in a fragment will be removed. If you want to remove a\nfragment, you have to explicitly set it to `null`. If the `c8y_ExternalId` is different to the existing Asset, it will be\nupdated accordingly. If the new `c8y_ExternalId` already exists, a conflict error will be returned.\n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_UPDATE` **AND** `ROLE_INVENTORY_ADMIN`*",
|
|
141847
|
+
"description": "Updates an existing asset. All properties of the asset can be updated, including `name`, `type`,\n`c8y_ExternalId`, and fragments - except the `id`. Missing properties in the request body will not be removed but stay\nunchanged. Fragments, however, will be replaced completely, so missing properties in a fragment will be removed. If you want to remove a\nfragment, you have to explicitly set it to `null`. If the `c8y_ExternalId` is different to the existing Asset, it will be\nupdated accordingly. If the new `c8y_ExternalId` already exists, a conflict error will be returned.\n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_UPDATE` **AND** `ROLE_INVENTORY_ADMIN`* (depending on the setting of `assets.permission.mode` as described in the Permissions section) ",
|
|
141416
141848
|
"operationId": "updateAsset",
|
|
141417
141849
|
"parameters": [{
|
|
141418
141850
|
"name": "assetId",
|
|
@@ -141440,14 +141872,34 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141440
141872
|
"delete": {
|
|
141441
141873
|
"tags": ["Assets"],
|
|
141442
141874
|
"summary": "Delete an existing asset",
|
|
141443
|
-
"description": "Deletes an existing asset.\n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`*",
|
|
141875
|
+
"description": "Deletes an existing asset and optionally its hierarchy consisting of all assigned sub-assets and/or all assigned devices. Sub-assets and\ndevices which are also assigned to other parent assets outside the hierarchy, will not be deleted but only unassigned from the deleted asset.\n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`* (depending on the setting of `assets.permission.mode` as described in the Permissions section) ",
|
|
141444
141876
|
"operationId": "deleteAsset",
|
|
141445
|
-
"parameters": [
|
|
141446
|
-
|
|
141447
|
-
|
|
141448
|
-
|
|
141449
|
-
|
|
141450
|
-
|
|
141877
|
+
"parameters": [
|
|
141878
|
+
{
|
|
141879
|
+
"name": "assetId",
|
|
141880
|
+
"in": "path",
|
|
141881
|
+
"required": true,
|
|
141882
|
+
"schema": { "type": "string" }
|
|
141883
|
+
},
|
|
141884
|
+
{
|
|
141885
|
+
"name": "deleteSubAssets",
|
|
141886
|
+
"in": "query",
|
|
141887
|
+
"description": "Indicates whether the resource also deletes sub-assets. If `false`, sub-assets are only unassigned from the deleted asset but not deleted themselves.",
|
|
141888
|
+
"schema": {
|
|
141889
|
+
"type": "boolean",
|
|
141890
|
+
"default": true
|
|
141891
|
+
}
|
|
141892
|
+
},
|
|
141893
|
+
{
|
|
141894
|
+
"name": "deleteDevices",
|
|
141895
|
+
"in": "query",
|
|
141896
|
+
"description": "Indicates whether the resource also deletes devices (managed objects with the `c8y_IsDevice` fragment) whose all parent assets are being deleted.",
|
|
141897
|
+
"schema": {
|
|
141898
|
+
"type": "boolean",
|
|
141899
|
+
"default": false
|
|
141900
|
+
}
|
|
141901
|
+
}
|
|
141902
|
+
],
|
|
141451
141903
|
"responses": { "204": { "description": "No Content" } }
|
|
141452
141904
|
}
|
|
141453
141905
|
},
|
|
@@ -141455,7 +141907,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141455
141907
|
"get": {
|
|
141456
141908
|
"tags": ["Linked Series"],
|
|
141457
141909
|
"summary": "Retrieve the source for a given linked series",
|
|
141458
|
-
"description": "Retrieves the source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`.",
|
|
141910
|
+
"description": "Retrieves the source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`. ",
|
|
141459
141911
|
"operationId": "getLinkedSeriesSource",
|
|
141460
141912
|
"parameters": [
|
|
141461
141913
|
{
|
|
@@ -141485,7 +141937,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141485
141937
|
"put": {
|
|
141486
141938
|
"tags": ["Linked Series"],
|
|
141487
141939
|
"summary": "Update the source for a given linked series",
|
|
141488
|
-
"description": "Updates an existing source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_UPDATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` or `ROLE_DIGITAL_TWIN_LINKING_UPDATE` or `ROLE_DIGITAL_TWIN_LINKING_ADMIN`*.",
|
|
141940
|
+
"description": "Updates an existing source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_UPDATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` or `ROLE_DIGITAL_TWIN_LINKING_UPDATE` or `ROLE_DIGITAL_TWIN_LINKING_ADMIN`*. ",
|
|
141489
141941
|
"operationId": "updateLinkedSeriesSource",
|
|
141490
141942
|
"parameters": [
|
|
141491
141943
|
{
|
|
@@ -141528,7 +141980,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141528
141980
|
"post": {
|
|
141529
141981
|
"tags": ["Linked Series"],
|
|
141530
141982
|
"summary": "Set the source for a given linked series",
|
|
141531
|
-
"description": "Adds a new source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_CREATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` or `ROLE_DIGITAL_TWIN_LINKING_CREATE` or `ROLE_DIGITAL_TWIN_LINKING_ADMIN`*.",
|
|
141983
|
+
"description": "Adds a new source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_CREATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` or `ROLE_DIGITAL_TWIN_LINKING_CREATE` or `ROLE_DIGITAL_TWIN_LINKING_ADMIN`*. ",
|
|
141532
141984
|
"operationId": "createLinkedSeriesSource",
|
|
141533
141985
|
"parameters": [
|
|
141534
141986
|
{
|
|
@@ -141571,7 +142023,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141571
142023
|
"delete": {
|
|
141572
142024
|
"tags": ["Linked Series"],
|
|
141573
142025
|
"summary": "Delete the source for a given linked series",
|
|
141574
|
-
"description": "Deletes an existing source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` or `ROLE_DIGITAL_TWIN_LINKING_ADMIN`*.",
|
|
142026
|
+
"description": "Deletes an existing source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` or `ROLE_DIGITAL_TWIN_LINKING_ADMIN`*. ",
|
|
141575
142027
|
"operationId": "deleteLinkedSeriesSource",
|
|
141576
142028
|
"parameters": [
|
|
141577
142029
|
{
|
|
@@ -141599,7 +142051,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141599
142051
|
"/service/dtm/assets/{assetId}/linkedSeries/{fragment}/{series}/source/type": { "put": {
|
|
141600
142052
|
"tags": ["Linked Series"],
|
|
141601
142053
|
"summary": "Update the measurement type of a linked series source",
|
|
141602
|
-
"description": "Modifies the `type` of a source of a linked series for a given asset. If the `fetchTypeFromSource` is set to\n`true`, the type specified in the body of the request will be ignored. Instead, the value is fetched from the last measurement of\nthe device defined by the which `source.id
|
|
142054
|
+
"description": "Modifies the `type` of a source of a linked series for a given asset. If the `fetchTypeFromSource` is set to\n`true`, the type specified in the body of the request will be ignored. Instead, the value is fetched from the last measurement of\nthe device defined by the which `source.id`.\n\n Using `content-type=application/json` is deprecated, use `content-type=text/plain` instead. ",
|
|
141603
142055
|
"operationId": "updateMeasurementType",
|
|
141604
142056
|
"parameters": [
|
|
141605
142057
|
{
|
|
@@ -141630,16 +142082,61 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141630
142082
|
}
|
|
141631
142083
|
}
|
|
141632
142084
|
],
|
|
141633
|
-
"requestBody": { "content": {
|
|
142085
|
+
"requestBody": { "content": {
|
|
142086
|
+
"text/plain": { "schema": { "type": "string" } },
|
|
142087
|
+
"application/json": { "schema": { "type": "string" } }
|
|
142088
|
+
} },
|
|
141634
142089
|
"responses": { "200": {
|
|
141635
142090
|
"description": "OK",
|
|
141636
142091
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Source" } } }
|
|
141637
142092
|
} }
|
|
141638
142093
|
} },
|
|
142094
|
+
"/service/dtm/assets/{assetId}/linkedSeries/{fragment}/{series}/reconcileOpposite": { "put": {
|
|
142095
|
+
"tags": ["Linked Series"],
|
|
142096
|
+
"summary": "Reconcile the opposite link for a linked series",
|
|
142097
|
+
"description": "Reconciles the opposite link (MeasurementSourceLink) for a given linked series.\n\n Recreates or updates the LinkedAsset representation in the device's MeasurementSourceLink (c8y_LinkedSeriesReverseIndex) referenced by the LinkedSeries' `source.id`, ensuring bidirectional link consistency. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_UPDATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN`*. ",
|
|
142098
|
+
"operationId": "reconcileOppositeLink",
|
|
142099
|
+
"parameters": [
|
|
142100
|
+
{
|
|
142101
|
+
"name": "assetId",
|
|
142102
|
+
"in": "path",
|
|
142103
|
+
"required": true,
|
|
142104
|
+
"schema": { "type": "string" }
|
|
142105
|
+
},
|
|
142106
|
+
{
|
|
142107
|
+
"name": "fragment",
|
|
142108
|
+
"in": "path",
|
|
142109
|
+
"required": true,
|
|
142110
|
+
"schema": { "type": "string" }
|
|
142111
|
+
},
|
|
142112
|
+
{
|
|
142113
|
+
"name": "series",
|
|
142114
|
+
"in": "path",
|
|
142115
|
+
"required": true,
|
|
142116
|
+
"schema": { "type": "string" }
|
|
142117
|
+
},
|
|
142118
|
+
{
|
|
142119
|
+
"name": "removeMissingSourceId",
|
|
142120
|
+
"in": "query",
|
|
142121
|
+
"description": "Indicates the resource to remove the `id` of the source of the Linked Series if the device with the given id does not exist. This can be used to automatically clean up Linked Series with non-existing sources.",
|
|
142122
|
+
"schema": {
|
|
142123
|
+
"type": "boolean",
|
|
142124
|
+
"default": false
|
|
142125
|
+
}
|
|
142126
|
+
}
|
|
142127
|
+
],
|
|
142128
|
+
"responses": {
|
|
142129
|
+
"200": {
|
|
142130
|
+
"description": "OK",
|
|
142131
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkedAsset" } } }
|
|
142132
|
+
},
|
|
142133
|
+
"204": { "description": "No Content" }
|
|
142134
|
+
}
|
|
142135
|
+
} },
|
|
141639
142136
|
"/service/dtm/assets/{assetId}/linkedSeries/{fragment}/{series}/label": { "put": {
|
|
141640
142137
|
"tags": ["Linked Series"],
|
|
141641
142138
|
"summary": "Update the label of a linked series",
|
|
141642
|
-
"description": "Modifies the `label` of a linked series for a given asset.",
|
|
142139
|
+
"description": "Modifies the `label` of a linked series for a given asset.\n\n Using `content-type=application/json` is deprecated, use `content-type=text/plain` instead. ",
|
|
141643
142140
|
"operationId": "updateLabel",
|
|
141644
142141
|
"parameters": [
|
|
141645
142142
|
{
|
|
@@ -141662,7 +142159,10 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141662
142159
|
}
|
|
141663
142160
|
],
|
|
141664
142161
|
"requestBody": {
|
|
141665
|
-
"content": {
|
|
142162
|
+
"content": {
|
|
142163
|
+
"text/plain": { "schema": { "type": "string" } },
|
|
142164
|
+
"application/json": { "schema": { "type": "string" } }
|
|
142165
|
+
},
|
|
141666
142166
|
"required": true
|
|
141667
142167
|
},
|
|
141668
142168
|
"responses": { "200": {
|
|
@@ -141670,11 +142170,41 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141670
142170
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkedSeries" } } }
|
|
141671
142171
|
} }
|
|
141672
142172
|
} },
|
|
142173
|
+
"/service/dtm/assets/{assetId}/linkedSeries/reconcileOpposite": { "put": {
|
|
142174
|
+
"tags": ["Linked Series"],
|
|
142175
|
+
"summary": "Reconcile the opposite links for all linked series of an asset",
|
|
142176
|
+
"description": "Reconciles the opposite links (MeasurementSourceLink) for all LinkedSeries with a `source.id` in the given asset.\n\n For each LinkedSeries with a `source.id`, recreates or updates the LinkedAsset representation in the device's MeasurementSourceLink (c8y_LinkedSeriesReverseIndex), ensuring bidirectional link consistency across the whole asset. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_UPDATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN`*. ",
|
|
142177
|
+
"operationId": "reconcileOppositeLinks",
|
|
142178
|
+
"parameters": [{
|
|
142179
|
+
"name": "assetId",
|
|
142180
|
+
"in": "path",
|
|
142181
|
+
"required": true,
|
|
142182
|
+
"schema": { "type": "string" }
|
|
142183
|
+
}, {
|
|
142184
|
+
"name": "removeMissingSourceId",
|
|
142185
|
+
"in": "query",
|
|
142186
|
+
"description": "Indicates the resource to remove the `id` of the source of the Linked Series if the device with the given id does not exist. This can be used to automatically clean up Linked Series with non-existing sources.",
|
|
142187
|
+
"schema": {
|
|
142188
|
+
"type": "boolean",
|
|
142189
|
+
"default": false
|
|
142190
|
+
}
|
|
142191
|
+
}],
|
|
142192
|
+
"responses": {
|
|
142193
|
+
"200": {
|
|
142194
|
+
"description": "OK",
|
|
142195
|
+
"content": { "application/json": { "schema": {
|
|
142196
|
+
"type": "array",
|
|
142197
|
+
"items": { "$ref": "#/components/schemas/LinkedAsset" }
|
|
142198
|
+
} } }
|
|
142199
|
+
},
|
|
142200
|
+
"204": { "description": "No Content" }
|
|
142201
|
+
}
|
|
142202
|
+
} },
|
|
141673
142203
|
"/service/dtm/definitions/properties": {
|
|
141674
142204
|
"get": {
|
|
141675
142205
|
"tags": ["Property Definitions"],
|
|
141676
142206
|
"summary": "Retrieve all Property Definitions",
|
|
141677
|
-
"description": "Finds a collection of `Property Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Property Definition`s.\n\n * Passing only one `title` results in `0...1` `Property Definition`s.\n\n\n\n\n\n The following rules will be applied when searching for the `Property Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `<i>Will be executed in context of an `Storage` tenant.</i>",
|
|
142207
|
+
"description": "Finds a collection of `Property Definition`s.\n\n This resource offers diverse filtering capabilities, yet it's essential to consider data consistency rules: \n\n * Passing only one `identifier` results in `0...1` `Property Definition`s.\n\n * Passing only one `title` results in `0...1` `Property Definition`s.\n\n \n\n \n\n The following rules will be applied when searching for the `Property Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `<i>Will be executed in context of an `Storage` tenant.</i>",
|
|
141678
142208
|
"operationId": "getPropertyDefinitions",
|
|
141679
142209
|
"parameters": [
|
|
141680
142210
|
{
|
|
@@ -141739,7 +142269,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141739
142269
|
{
|
|
141740
142270
|
"name": "applicableTo",
|
|
141741
142271
|
"in": "query",
|
|
141742
|
-
"description": "Limits the response to Property Definitions applicable to the specified domain entity.",
|
|
142272
|
+
"description": "Limits the response to the Property Definitions that are applicable to the specified domain entity.",
|
|
141743
142273
|
"schema": {
|
|
141744
142274
|
"type": "string",
|
|
141745
142275
|
"enum": [
|
|
@@ -141758,9 +142288,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141758
142288
|
"in": "query",
|
|
141759
142289
|
"description": "The current page number to be retrieved.",
|
|
141760
142290
|
"schema": {
|
|
141761
|
-
"
|
|
141762
|
-
"
|
|
141763
|
-
"default": 1
|
|
142291
|
+
"minimum": 1,
|
|
142292
|
+
"type": "integer"
|
|
141764
142293
|
}
|
|
141765
142294
|
},
|
|
141766
142295
|
{
|
|
@@ -141782,7 +142311,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141782
142311
|
"post": {
|
|
141783
142312
|
"tags": ["Property Definitions"],
|
|
141784
142313
|
"summary": "Create a new Property Definition",
|
|
141785
|
-
"description": "Creates a new `Property Definition`.\n\n This operation ensures consistency by enforcing the following rules: \n\n * The `identifier` of the `Property Definition` must be unique for the given `context`s.\n\n * If a `title` is provided, it must also be unique.\n\n * None of the `context`s must not be used by another `Property Definition` with the same `identifier`.\n\n\n\n\n\n The supplied JSON Schema must conform to the [JSON Schema Draft-7 specification](http://json-schema.org/draft-07/schema).",
|
|
142314
|
+
"description": "Creates a new `Property Definition`.\n\n This operation ensures consistency by enforcing the following rules: \n\n * The `identifier` of the `Property Definition` must be unique for the given `context`s.\n\n * If a `title` is provided, it must also be unique.\n\n * None of the `context`s must not be used by another `Property Definition` with the same `identifier`.\n\n \n\n\n\n The supplied JSON Schema must conform to the [JSON Schema Draft-7 specification](http://json-schema.org/draft-07/schema). ",
|
|
141786
142315
|
"operationId": "createPropertyDefinition",
|
|
141787
142316
|
"requestBody": {
|
|
141788
142317
|
"description": "The data payload representing the `Property Definition` to be created.",
|
|
@@ -141798,7 +142327,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141798
142327
|
"/service/dtm/definitions/properties/compose": { "post": {
|
|
141799
142328
|
"tags": ["Property Definitions"],
|
|
141800
142329
|
"summary": "Create JSON schema from Property Definitions",
|
|
141801
|
-
"description": "Creates a JSON schema object type.\n\n This operation constructs a JSON Schema object type that encapsulates one or more `Property Definition`s as its properties. The resulting schema will include all provided `Property Definition`s. \n\n The `Property Definition`s to be included are supplied in the request body. At least one `Property Definition` must be provided and identified by its `identifier`. If a `Property Definition` applies to a specific domain entity, a corresponding `context` must also be supplied.",
|
|
142330
|
+
"description": "Creates a JSON schema object type.\n\n This operation constructs a JSON Schema object type that encapsulates one or more `Property Definition`s as its properties. The resulting schema will include all provided `Property Definition`s. \n\n The `Property Definition`s to be included are supplied in the request body. At least one `Property Definition` must be provided and identified by its `identifier`. If a `Property Definition` applies to a specific domain entity, a corresponding `context` must also be supplied. ",
|
|
141802
142331
|
"operationId": "composePropertyDefinitions",
|
|
141803
142332
|
"requestBody": {
|
|
141804
142333
|
"content": { "application/json": { "schema": {
|
|
@@ -141816,7 +142345,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141816
142345
|
"get": {
|
|
141817
142346
|
"tags": ["Assets"],
|
|
141818
142347
|
"summary": "Retrieve assets",
|
|
141819
|
-
"description": "Retrieves all assets registered in your tenant.\n\n The `query` parameter supports a flexible syntax for filtering and ordering results.",
|
|
142348
|
+
"description": "Retrieves all assets registered in your tenant.\n\n The `query` parameter supports a flexible syntax for filtering and ordering results. ",
|
|
141820
142349
|
"operationId": "getAssets",
|
|
141821
142350
|
"parameters": [
|
|
141822
142351
|
{
|
|
@@ -141854,7 +142383,17 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141854
142383
|
{
|
|
141855
142384
|
"name": "withSubAssets",
|
|
141856
142385
|
"in": "query",
|
|
141857
|
-
"description": "Indicates the resource to add the sub-assets in the response.",
|
|
142386
|
+
"description": "**Deprecated** – use the dedicated `/assets/{assetId}/subAssets`, `/assets/externalIds/{externalId}/subAssets` or `/assets/subAssets` endpoint instead!<br>Indicates the resource to add the sub-assets in the response.",
|
|
142387
|
+
"deprecated": true,
|
|
142388
|
+
"schema": {
|
|
142389
|
+
"type": "boolean",
|
|
142390
|
+
"default": false
|
|
142391
|
+
}
|
|
142392
|
+
},
|
|
142393
|
+
{
|
|
142394
|
+
"name": "withParents",
|
|
142395
|
+
"in": "query",
|
|
142396
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
141858
142397
|
"schema": {
|
|
141859
142398
|
"type": "boolean",
|
|
141860
142399
|
"default": false
|
|
@@ -141868,13 +142407,30 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141868
142407
|
"default": false
|
|
141869
142408
|
}
|
|
141870
142409
|
},
|
|
142410
|
+
{
|
|
142411
|
+
"name": "includeGroups",
|
|
142412
|
+
"in": "query",
|
|
142413
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
142414
|
+
"schema": {
|
|
142415
|
+
"type": "boolean",
|
|
142416
|
+
"default": false
|
|
142417
|
+
}
|
|
142418
|
+
},
|
|
142419
|
+
{
|
|
142420
|
+
"name": "withChildrenCount",
|
|
142421
|
+
"in": "query",
|
|
142422
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
142423
|
+
"schema": {
|
|
142424
|
+
"type": "boolean",
|
|
142425
|
+
"default": false
|
|
142426
|
+
}
|
|
142427
|
+
},
|
|
141871
142428
|
{
|
|
141872
142429
|
"name": "currentPage",
|
|
141873
142430
|
"in": "query",
|
|
141874
142431
|
"schema": {
|
|
141875
|
-
"
|
|
141876
|
-
"
|
|
141877
|
-
"default": 1
|
|
142432
|
+
"minimum": 1,
|
|
142433
|
+
"type": "integer"
|
|
141878
142434
|
}
|
|
141879
142435
|
},
|
|
141880
142436
|
{
|
|
@@ -141895,7 +142451,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141895
142451
|
"post": {
|
|
141896
142452
|
"tags": ["Assets"],
|
|
141897
142453
|
"summary": "Create or update an asset",
|
|
141898
|
-
"description": "Creates or updates an Asset, for example, a room within a building.\n\n In general, each asset may consist of: \n\n * The name of the asset.\n\n * The most specific type of the asset.\n\n * Fragments with specific meanings, for example, c8y_Position, c8y_SupportedOperations.\n\n * Fragment `c8y_ExternalId` which uniquely identifies the asset in an external system.\n\n * If a value for the property `id` is provided, it will be ignored.\n\n\n\n If an asset with the given external ID exists and `X-Upsert-Mode` is true, the asset will be updated and the response code will be 200. If an asset with the given external ID exists and `X-Upsert-Mode` is false, a conflict error will be returned. If no asset with the given external ID exists, a new asset will be created (independent of the `X-Upsert-Mode`) and the response code will be 201.",
|
|
142454
|
+
"description": "Creates or updates an Asset, for example, a room within a building.\n\n In general, each asset may consist of: \n\n * The name of the asset.\n\n * The most specific type of the asset.\n\n * Fragments with specific meanings, for example, c8y_Position, c8y_SupportedOperations.\n\n * Fragment `c8y_ExternalId` which uniquely identifies the asset in an external system.\n\n * If a value for the property `id` is provided, it will be ignored.\n\n \n\n If an asset with the given external ID exists and `X-Upsert-Mode` is true, the asset will be updated and the response code will be 200. If an asset with the given external ID exists and `X-Upsert-Mode` is false, a conflict error will be returned. If no asset with the given external ID exists, a new asset will be created (independent of the `X-Upsert-Mode`) and the response code will be 201. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_CREATE` **AND** `ROLE_INVENTORY_ADMIN`* (depending on the setting of `assets.permission.mode` as described in the Permissions section) ",
|
|
141899
142455
|
"operationId": "createAsset",
|
|
141900
142456
|
"parameters": [{
|
|
141901
142457
|
"name": "fetchTypeFromSource",
|
|
@@ -141927,11 +142483,127 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141927
142483
|
}
|
|
141928
142484
|
}
|
|
141929
142485
|
},
|
|
142486
|
+
"/service/dtm/assets/{assetId}/subAssets": {
|
|
142487
|
+
"get": {
|
|
142488
|
+
"tags": ["Assets"],
|
|
142489
|
+
"summary": "Retrieve sub-assets of an asset",
|
|
142490
|
+
"description": "Retrieves sub-assets of an asset.\n\n Fetches all sub-assets of the parent asset specified by `assetId`. ",
|
|
142491
|
+
"operationId": "getSubAssets",
|
|
142492
|
+
"parameters": [
|
|
142493
|
+
{
|
|
142494
|
+
"name": "assetId",
|
|
142495
|
+
"in": "path",
|
|
142496
|
+
"required": true,
|
|
142497
|
+
"schema": { "type": "string" }
|
|
142498
|
+
},
|
|
142499
|
+
{
|
|
142500
|
+
"name": "query",
|
|
142501
|
+
"in": "query",
|
|
142502
|
+
"description": "Use the `$filter` keyword to specify filtering criteria. Filtering can be applied to properties of the asset. Detailed information can be found within the Cumulocity core OpenAPI [here](https://cumulocity.com/api/core/#tag/Query-language).",
|
|
142503
|
+
"schema": {
|
|
142504
|
+
"type": "string",
|
|
142505
|
+
"format": "c8y:query"
|
|
142506
|
+
},
|
|
142507
|
+
"examples": {
|
|
142508
|
+
"Filter by name": {
|
|
142509
|
+
"description": "Filter by name",
|
|
142510
|
+
"value": "$filter=name eq 'Windfarm'"
|
|
142511
|
+
},
|
|
142512
|
+
"Filter by type": {
|
|
142513
|
+
"description": "Filter by type",
|
|
142514
|
+
"value": "$filter=type eq 'c8y_*'"
|
|
142515
|
+
},
|
|
142516
|
+
"Using orderby": {
|
|
142517
|
+
"description": "Using orderby",
|
|
142518
|
+
"value": "$orderby=id asc"
|
|
142519
|
+
}
|
|
142520
|
+
}
|
|
142521
|
+
},
|
|
142522
|
+
{
|
|
142523
|
+
"name": "withLinkedSeries",
|
|
142524
|
+
"in": "query",
|
|
142525
|
+
"description": "Indicates the resource to add the `c8y_LinkedSeries` fragment in the response.",
|
|
142526
|
+
"schema": {
|
|
142527
|
+
"type": "boolean",
|
|
142528
|
+
"default": false
|
|
142529
|
+
}
|
|
142530
|
+
},
|
|
142531
|
+
{
|
|
142532
|
+
"name": "withParents",
|
|
142533
|
+
"in": "query",
|
|
142534
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
142535
|
+
"schema": {
|
|
142536
|
+
"type": "boolean",
|
|
142537
|
+
"default": false
|
|
142538
|
+
}
|
|
142539
|
+
},
|
|
142540
|
+
{
|
|
142541
|
+
"name": "includeGroups",
|
|
142542
|
+
"in": "query",
|
|
142543
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
142544
|
+
"schema": {
|
|
142545
|
+
"type": "boolean",
|
|
142546
|
+
"default": false
|
|
142547
|
+
}
|
|
142548
|
+
},
|
|
142549
|
+
{
|
|
142550
|
+
"name": "withChildrenCount",
|
|
142551
|
+
"in": "query",
|
|
142552
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
142553
|
+
"schema": {
|
|
142554
|
+
"type": "boolean",
|
|
142555
|
+
"default": false
|
|
142556
|
+
}
|
|
142557
|
+
},
|
|
142558
|
+
{
|
|
142559
|
+
"name": "currentPage",
|
|
142560
|
+
"in": "query",
|
|
142561
|
+
"schema": {
|
|
142562
|
+
"minimum": 1,
|
|
142563
|
+
"type": "integer"
|
|
142564
|
+
}
|
|
142565
|
+
},
|
|
142566
|
+
{
|
|
142567
|
+
"name": "pageSize",
|
|
142568
|
+
"in": "query",
|
|
142569
|
+
"schema": {
|
|
142570
|
+
"maximum": 2e3,
|
|
142571
|
+
"minimum": 1,
|
|
142572
|
+
"type": "integer"
|
|
142573
|
+
}
|
|
142574
|
+
}
|
|
142575
|
+
],
|
|
142576
|
+
"responses": { "200": {
|
|
142577
|
+
"description": "OK",
|
|
142578
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedAssetList" } } }
|
|
142579
|
+
} }
|
|
142580
|
+
},
|
|
142581
|
+
"post": {
|
|
142582
|
+
"tags": ["Assets"],
|
|
142583
|
+
"summary": "Create and assign a sub-asset in one operation",
|
|
142584
|
+
"description": "Creates a new sub-asset and assigns it to the parent asset in one operation.\n\n Creates a new asset from the provided payload and immediately assigns it as a sub-asset to the specified parent asset. If the parent asset does not exist, the operation responds with `HTTP 404`. If the parent asset or the payload is not of type Asset, the operation responds with `HTTP 422`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`* (depending on the setting of `assets.permission.mode` as described in the Permissions section) ",
|
|
142585
|
+
"operationId": "createAndAssignSubAsset",
|
|
142586
|
+
"parameters": [{
|
|
142587
|
+
"name": "assetId",
|
|
142588
|
+
"in": "path",
|
|
142589
|
+
"required": true,
|
|
142590
|
+
"schema": { "type": "string" }
|
|
142591
|
+
}],
|
|
142592
|
+
"requestBody": {
|
|
142593
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Asset" } } },
|
|
142594
|
+
"required": true
|
|
142595
|
+
},
|
|
142596
|
+
"responses": { "201": {
|
|
142597
|
+
"description": "Created",
|
|
142598
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Asset" } } }
|
|
142599
|
+
} }
|
|
142600
|
+
}
|
|
142601
|
+
},
|
|
141930
142602
|
"/service/dtm/assets/{assetId}/subAssets/{subAssetId}": {
|
|
141931
142603
|
"post": {
|
|
141932
142604
|
"tags": ["Assets"],
|
|
141933
142605
|
"summary": "Assign an asset as a sub-asset",
|
|
141934
|
-
"description": "Assigns an asset as a sub-asset.\n\n Associates the specified asset, identified by its ID, with a parent asset. The sub-asset must already exist. If one of the IDs does not exist, the operation responds with `HTTP 404`. If one of the IDs is not an Asset, the operation responds with `HTTP 422`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`*",
|
|
142606
|
+
"description": "Assigns an asset as a sub-asset.\n\n Associates the specified asset, identified by its ID, with a parent asset. The sub-asset must already exist. If one of the IDs does not exist, the operation responds with `HTTP 404`. If one of the IDs is not an Asset, the operation responds with `HTTP 422`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`* (depending on the setting of `assets.permission.mode` as described in the Permissions section) ",
|
|
141935
142607
|
"operationId": "assignSubAsset",
|
|
141936
142608
|
"parameters": [{
|
|
141937
142609
|
"name": "assetId",
|
|
@@ -141952,7 +142624,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141952
142624
|
"delete": {
|
|
141953
142625
|
"tags": ["Assets"],
|
|
141954
142626
|
"summary": "Remove a sub-asset from its parent asset",
|
|
141955
|
-
"description": "Removes a sub-asset from its parent asset.\n\n Removes the association of the specified asset, identified by its ID, with a parent asset. If one of the IDs does not exist, the operation responds with `HTTP 404`. If one of the IDs is not an Asset, the operation responds with `HTTP 422`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`*",
|
|
142627
|
+
"description": "Removes a sub-asset from its parent asset.\n\n Removes the association of the specified asset, identified by its ID, with a parent asset. If one of the IDs does not exist, the operation responds with `HTTP 404`. If one of the IDs is not an Asset, the operation responds with `HTTP 422`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN`* (depending on the setting of `assets.permission.mode` as described in the Permissions section) ",
|
|
141956
142628
|
"operationId": "unassignSubAsset",
|
|
141957
142629
|
"parameters": [{
|
|
141958
142630
|
"name": "assetId",
|
|
@@ -141972,7 +142644,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141972
142644
|
"get": {
|
|
141973
142645
|
"tags": ["Linked Series"],
|
|
141974
142646
|
"summary": "Retrieve linked series for an asset",
|
|
141975
|
-
"description": "Retrieves linked series for a given asset.\n\n Supports optionally searching for a specific `fragment` and/or `series`. \n\n The `withMeasurementType` query parameter is used to explicitly query the `type` of the measurements which are linked via the `source.id`. This parameter is only relevant if only one `LinkedSeries` is to be returned.",
|
|
142647
|
+
"description": "Retrieves linked series for a given asset.\n\n Supports optionally searching for a specific `fragment` and/or `series`. \n\n The `withMeasurementType` query parameter is used to explicitly query the `type` of the measurements which are linked via the `source.id`. This parameter is only relevant if only one `LinkedSeries` is to be returned. ",
|
|
141976
142648
|
"operationId": "getLinkedSeries",
|
|
141977
142649
|
"parameters": [
|
|
141978
142650
|
{
|
|
@@ -142005,7 +142677,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142005
142677
|
{
|
|
142006
142678
|
"name": "withJsonSchema",
|
|
142007
142679
|
"in": "query",
|
|
142008
|
-
"description": "If true
|
|
142680
|
+
"description": "If `true`, the response includes the inferred JSON schema describing all linked series of this Asset.",
|
|
142009
142681
|
"schema": {
|
|
142010
142682
|
"type": "boolean",
|
|
142011
142683
|
"default": false
|
|
@@ -142015,9 +142687,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142015
142687
|
"name": "currentPage",
|
|
142016
142688
|
"in": "query",
|
|
142017
142689
|
"schema": {
|
|
142018
|
-
"
|
|
142019
|
-
"
|
|
142020
|
-
"default": 1
|
|
142690
|
+
"minimum": 1,
|
|
142691
|
+
"type": "integer"
|
|
142021
142692
|
}
|
|
142022
142693
|
},
|
|
142023
142694
|
{
|
|
@@ -142044,7 +142715,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142044
142715
|
"post": {
|
|
142045
142716
|
"tags": ["Linked Series"],
|
|
142046
142717
|
"summary": "Add new and update existing linked series",
|
|
142047
|
-
"description": "Adds new and updates existing linked series for a given asset.\n\n Linked series are identified by their `fragment` **and** `series`. \n\n For each LinkedSeries provided in the body: \n\n * Adds the new linked series when it does not exist.\n\n * Updates the linked series when it already exists.\n\n\n\n\n\n *Required user role for adding a linked series: `ROLE_DIGITAL_TWIN_ASSETS_CREATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN`*.\n\n *Required user role for updating a linked series: `ROLE_DIGITAL_TWIN_ASSETS_UPDATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN`*.",
|
|
142718
|
+
"description": "Adds new and updates existing linked series for a given asset.\n\n Linked series are identified by their `fragment` **and** `series`. \n\n For each LinkedSeries provided in the body: \n\n * Adds the new linked series when it does not exist.\n\n * Updates the linked series when it already exists.\n\n \n\n \n\n The update behavior preserves the existing `source.id` when the Linked Series to be updated does not explicitly provide a non-blank `source.id` in the request body. If the `source.id` is provided, it will be updated accordingly. \n\n *Required user role for adding a linked series: `ROLE_DIGITAL_TWIN_ASSETS_CREATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN`*.\n\n *Required user role for updating a linked series: `ROLE_DIGITAL_TWIN_ASSETS_UPDATE` or `ROLE_DIGITAL_TWIN_ASSETS_ADMIN`*. ",
|
|
142048
142719
|
"operationId": "addAndUpdateLinkedSeries",
|
|
142049
142720
|
"parameters": [{
|
|
142050
142721
|
"name": "assetId",
|
|
@@ -142075,7 +142746,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142075
142746
|
"delete": {
|
|
142076
142747
|
"tags": ["Linked Series"],
|
|
142077
142748
|
"summary": "Delete a specific linked series",
|
|
142078
|
-
"description": "Deletes a linked series for a given asset.\n\n Deletes all linked series. Specify `fragment` and/or `series` as query parameters to be more specific about the linked series to be deleted. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN`*.",
|
|
142749
|
+
"description": "Deletes a linked series for a given asset.\n\n Deletes all linked series. Specify `fragment` and/or `series` as query parameters to be more specific about the linked series to be deleted. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN`*. ",
|
|
142079
142750
|
"operationId": "deleteLinkedSeries",
|
|
142080
142751
|
"parameters": [
|
|
142081
142752
|
{
|
|
@@ -142104,7 +142775,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142104
142775
|
"post": {
|
|
142105
142776
|
"tags": ["Assets"],
|
|
142106
142777
|
"summary": "Assign a device to an asset",
|
|
142107
|
-
"description": "Assigns a device to an asset.\n\n Associates the specified device, identified by its ID, with an asset. The device must already exist. If one of the IDs does not exist, the operation responds with `HTTP 404`. If the managed object for the assetId is not an asset or
|
|
142778
|
+
"description": "Assigns a device to an asset.\n\n Associates the specified device, identified by its ID, with an asset. The device must already exist. If one of the IDs does not exist, the operation responds with `HTTP 404`. If the managed object for the assetId is not an asset or if the managed object for the deviceId is not a Device, the operation responds with `HTTP 422`. \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN` or `ROLE_DIGITAL_TWIN_LINKING_UPDATE`* (depending on the setting of `assets.permission.mode` as described in the Permissions section) ",
|
|
142108
142779
|
"operationId": "assignDevice",
|
|
142109
142780
|
"parameters": [{
|
|
142110
142781
|
"name": "assetId",
|
|
@@ -142125,7 +142796,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142125
142796
|
"delete": {
|
|
142126
142797
|
"tags": ["Assets"],
|
|
142127
142798
|
"summary": "Remove a device from an asset",
|
|
142128
|
-
"description": "Removes a device from an asset.\n\n Removes the association of the specified device, identified by its ID, with an asset. If one of the IDs does not exist, the operation responds with `HTTP 404`. If the managed object for the assetId is not an asset or
|
|
142799
|
+
"description": "Removes a device from an asset.\n\n Removes the association of the specified device, identified by its ID, with an asset. If one of the IDs does not exist, the operation responds with `HTTP 404`. If the managed object for the assetId is not an asset or if the managed object for the deviceId is not a Device, the operation responds with `HTTP 422`. \n\n \n\n *Required user role: `ROLE_DIGITAL_TWIN_ASSETS_ADMIN` **AND** `ROLE_INVENTORY_ADMIN` or `ROLE_DIGITAL_TWIN_LINKING_UPDATE`* (depending on the setting of `assets.permission.mode` as described in the Permissions section) ",
|
|
142129
142800
|
"operationId": "unassignDevice",
|
|
142130
142801
|
"parameters": [{
|
|
142131
142802
|
"name": "assetId",
|
|
@@ -142176,7 +142847,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142176
142847
|
"/service/dtm/definitions/properties/count": { "get": {
|
|
142177
142848
|
"tags": ["Property Definitions"],
|
|
142178
142849
|
"summary": "Get the count of all Property Definitions",
|
|
142179
|
-
"description": "Counts the collection of `Property Definition`s.\n\n The following rules will be applied when searching for the `Property Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
142850
|
+
"description": "Counts the collection of `Property Definition`s.\n\n The following rules will be applied when searching for the `Property Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
142180
142851
|
"operationId": "countPropertyDefinitions",
|
|
142181
142852
|
"parameters": [
|
|
142182
142853
|
{
|
|
@@ -142212,7 +142883,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142212
142883
|
{
|
|
142213
142884
|
"name": "applicableTo",
|
|
142214
142885
|
"in": "query",
|
|
142215
|
-
"description": "Limits the response to Property Definitions applicable to the specified domain entity.",
|
|
142886
|
+
"description": "Limits the response to the Property Definitions that are applicable to the specified domain entity.",
|
|
142216
142887
|
"schema": {
|
|
142217
142888
|
"type": "string",
|
|
142218
142889
|
"enum": [
|
|
@@ -142236,7 +142907,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142236
142907
|
"get": {
|
|
142237
142908
|
"tags": ["Measurement Definitions"],
|
|
142238
142909
|
"summary": "Retrieve a Measurement Definition by identifier",
|
|
142239
|
-
"description": "Finds a `Measurement Definition` by its `identifier`.\n\n The `identifier` needs to point to an existing `Measurement Definition`, otherwise this operation responds in `HTTP 404`.",
|
|
142910
|
+
"description": "Finds a `Measurement Definition` by its `identifier`.\n\n The `identifier` needs to point to an existing `Measurement Definition`, otherwise this operation responds in `HTTP 404`. ",
|
|
142240
142911
|
"operationId": "getMeasurementDefinition",
|
|
142241
142912
|
"parameters": [{
|
|
142242
142913
|
"name": "identifier",
|
|
@@ -142300,7 +142971,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142300
142971
|
"/service/dtm/definitions/measurements/count": { "get": {
|
|
142301
142972
|
"tags": ["Measurement Definitions"],
|
|
142302
142973
|
"summary": "Get the count of all Measurement Definitions",
|
|
142303
|
-
"description": "Counts the collection of `Measurement Definition`s.\n\n The following rules will be applied when searching for the `Measurement Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
142974
|
+
"description": "Counts the collection of `Measurement Definition`s.\n\n The following rules will be applied when searching for the `Measurement Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
142304
142975
|
"operationId": "countMeasurementDefinitions",
|
|
142305
142976
|
"parameters": [
|
|
142306
142977
|
{
|
|
@@ -142343,7 +143014,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142343
143014
|
"get": {
|
|
142344
143015
|
"tags": ["Event Definitions"],
|
|
142345
143016
|
"summary": "Retrieve an Event Definition by identifier",
|
|
142346
|
-
"description": "Finds a `Event Definition` by its `identifier`.\n\n The `identifier` needs to point to an existing `Event Definition`, otherwise this operation responds in `HTTP 404`.",
|
|
143017
|
+
"description": "Finds a `Event Definition` by its `identifier`.\n\n The `identifier` needs to point to an existing `Event Definition`, otherwise this operation responds in `HTTP 404`. ",
|
|
142347
143018
|
"operationId": "getEventDefinition",
|
|
142348
143019
|
"parameters": [{
|
|
142349
143020
|
"name": "identifier",
|
|
@@ -142407,7 +143078,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142407
143078
|
"/service/dtm/definitions/events/count": { "get": {
|
|
142408
143079
|
"tags": ["Event Definitions"],
|
|
142409
143080
|
"summary": "Get the count of all Event Definitions",
|
|
142410
|
-
"description": "Counts the collection of `Event Definition`s.\n\n The following rules will be applied when searching for the `Event Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
143081
|
+
"description": "Counts the collection of `Event Definition`s.\n\n The following rules will be applied when searching for the `Event Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
142411
143082
|
"operationId": "countEventDefinitions",
|
|
142412
143083
|
"parameters": [
|
|
142413
143084
|
{
|
|
@@ -142450,7 +143121,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142450
143121
|
"get": {
|
|
142451
143122
|
"tags": ["Asset Definitions"],
|
|
142452
143123
|
"summary": "Retrieve an Asset Definition by identifier",
|
|
142453
|
-
"description": "Finds a `Asset Definition` by its `identifier`.\n\n The `identifier` needs to point to an existing `Asset Definition`, otherwise this operation responds in `HTTP 404`.",
|
|
143124
|
+
"description": "Finds a `Asset Definition` by its `identifier`.\n\n The `identifier` needs to point to an existing `Asset Definition`, otherwise this operation responds in `HTTP 404`. ",
|
|
142454
143125
|
"operationId": "getAssetDefinition",
|
|
142455
143126
|
"parameters": [{
|
|
142456
143127
|
"name": "identifier",
|
|
@@ -142482,7 +143153,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142482
143153
|
"/service/dtm/definitions/assets/count": { "get": {
|
|
142483
143154
|
"tags": ["Asset Definitions"],
|
|
142484
143155
|
"summary": "Get count of Asset Definitions",
|
|
142485
|
-
"description": "Counts the collection of `Asset Definition`s.\n\n The following rules will be applied when searching for the `Asset Definition`s: \n\n * `identifiers`, `titles`, `onlyRoots` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
143156
|
+
"description": "Counts the collection of `Asset Definition`s.\n\n The following rules will be applied when searching for the `Asset Definition`s: \n\n * `identifiers`, `titles`, `onlyRoots` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
142486
143157
|
"operationId": "countAssetDefinitions",
|
|
142487
143158
|
"parameters": [
|
|
142488
143159
|
{
|
|
@@ -142534,7 +143205,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142534
143205
|
"get": {
|
|
142535
143206
|
"tags": ["Alarm Definitions"],
|
|
142536
143207
|
"summary": "Retrieve an Alarm Definition by identifier",
|
|
142537
|
-
"description": "Retrieves an `Alarm Definition` by its `identifier`.\n\n The `identifier` needs to point to an existing `Alarm Definition`, otherwise this operation respond in `HTTP 404`.",
|
|
143208
|
+
"description": "Retrieves an `Alarm Definition` by its `identifier`.\n\n The `identifier` needs to point to an existing `Alarm Definition`, otherwise this operation respond in `HTTP 404`. ",
|
|
142538
143209
|
"operationId": "getAlarmDefinition",
|
|
142539
143210
|
"parameters": [{
|
|
142540
143211
|
"name": "identifier",
|
|
@@ -142598,7 +143269,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142598
143269
|
"/service/dtm/definitions/alarms/count": { "get": {
|
|
142599
143270
|
"tags": ["Alarm Definitions"],
|
|
142600
143271
|
"summary": "Get count of Alarm Definitions",
|
|
142601
|
-
"description": "Counts the collection of `Alarm Definition`s.\n\n The following rules will be applied when searching for the `Alarm Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.*",
|
|
143272
|
+
"description": "Counts the collection of `Alarm Definition`s.\n\n The following rules will be applied when searching for the `Alarm Definition`s: \n\n * `identifiers`, `titles`, `tags` will be concatenated using logical AND operators\n\n * Values within a filter will be concatenated using a logical OR operator\n\n For instance, the query filter `?identifiers=id_a,id_b&title=position` results in the following query filter:` (identifier equals id_a OR identifier equals id_b) AND title equals position `\n\n *Will be executed in context of an `Storage` tenant.* ",
|
|
142602
143273
|
"operationId": "countAlarmDefinitions",
|
|
142603
143274
|
"parameters": [
|
|
142604
143275
|
{
|
|
@@ -142637,11 +143308,41 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142637
143308
|
"content": { "application/json": { "schema": { "type": "integer" } } }
|
|
142638
143309
|
} }
|
|
142639
143310
|
} },
|
|
142640
|
-
"/service/dtm/assets/{assetId}/
|
|
143311
|
+
"/service/dtm/assets/{assetId}/linkedSeries/count": { "get": {
|
|
143312
|
+
"tags": ["Linked Series"],
|
|
143313
|
+
"summary": "Get count of linked series for an asset",
|
|
143314
|
+
"description": "Counts linked series for a given asset.\n\n Provides a count of linked series, with optional `fragment` and `series` query parameters to filter and count specific linked series. ",
|
|
143315
|
+
"operationId": "countLinkedSeries",
|
|
143316
|
+
"parameters": [
|
|
143317
|
+
{
|
|
143318
|
+
"name": "assetId",
|
|
143319
|
+
"in": "path",
|
|
143320
|
+
"required": true,
|
|
143321
|
+
"schema": { "type": "string" }
|
|
143322
|
+
},
|
|
143323
|
+
{
|
|
143324
|
+
"name": "fragment",
|
|
143325
|
+
"in": "query",
|
|
143326
|
+
"description": "A characteristic which identifies the measurement.",
|
|
143327
|
+
"schema": { "type": "string" }
|
|
143328
|
+
},
|
|
143329
|
+
{
|
|
143330
|
+
"name": "series",
|
|
143331
|
+
"in": "query",
|
|
143332
|
+
"description": "The specific series to search for.",
|
|
143333
|
+
"schema": { "type": "string" }
|
|
143334
|
+
}
|
|
143335
|
+
],
|
|
143336
|
+
"responses": { "200": {
|
|
143337
|
+
"description": "OK",
|
|
143338
|
+
"content": { "application/json": { "schema": { "type": "integer" } } }
|
|
143339
|
+
} }
|
|
143340
|
+
} },
|
|
143341
|
+
"/service/dtm/assets/{assetId}/devices": { "get": {
|
|
142641
143342
|
"tags": ["Assets"],
|
|
142642
|
-
"summary": "Retrieve
|
|
142643
|
-
"description": "Retrieves
|
|
142644
|
-
"operationId": "
|
|
143343
|
+
"summary": "Retrieve child devices of an asset",
|
|
143344
|
+
"description": "Retrieves child devices of an asset.\n\n Fetches all child devices of the parent asset specified by `assetId`. ",
|
|
143345
|
+
"operationId": "getChildDevices",
|
|
142645
143346
|
"parameters": [
|
|
142646
143347
|
{
|
|
142647
143348
|
"name": "assetId",
|
|
@@ -142672,22 +143373,12 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142672
143373
|
}
|
|
142673
143374
|
}
|
|
142674
143375
|
},
|
|
142675
|
-
{
|
|
142676
|
-
"name": "withLinkedSeries",
|
|
142677
|
-
"in": "query",
|
|
142678
|
-
"description": "Indicates the resource to add the `c8y_LinkedSeries` fragment in the response.",
|
|
142679
|
-
"schema": {
|
|
142680
|
-
"type": "boolean",
|
|
142681
|
-
"default": false
|
|
142682
|
-
}
|
|
142683
|
-
},
|
|
142684
143376
|
{
|
|
142685
143377
|
"name": "currentPage",
|
|
142686
143378
|
"in": "query",
|
|
142687
143379
|
"schema": {
|
|
142688
|
-
"
|
|
142689
|
-
"
|
|
142690
|
-
"default": 1
|
|
143380
|
+
"minimum": 1,
|
|
143381
|
+
"type": "integer"
|
|
142691
143382
|
}
|
|
142692
143383
|
},
|
|
142693
143384
|
{
|
|
@@ -142702,50 +143393,26 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142702
143393
|
],
|
|
142703
143394
|
"responses": { "200": {
|
|
142704
143395
|
"description": "OK",
|
|
142705
|
-
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/
|
|
142706
|
-
} }
|
|
142707
|
-
} },
|
|
142708
|
-
"/service/dtm/assets/{assetId}/linkedSeries/count": { "get": {
|
|
142709
|
-
"tags": ["Linked Series"],
|
|
142710
|
-
"summary": "Get count of linked series for an asset",
|
|
142711
|
-
"description": "Counts linked series for a given asset.\n\n Provides a count of linked series, with optional `fragment` and `series` query parameters to filter and count specific linked series.",
|
|
142712
|
-
"operationId": "countLinkedSeries",
|
|
142713
|
-
"parameters": [
|
|
142714
|
-
{
|
|
142715
|
-
"name": "assetId",
|
|
142716
|
-
"in": "path",
|
|
142717
|
-
"required": true,
|
|
142718
|
-
"schema": { "type": "string" }
|
|
142719
|
-
},
|
|
142720
|
-
{
|
|
142721
|
-
"name": "fragment",
|
|
142722
|
-
"in": "query",
|
|
142723
|
-
"description": "A characteristic which identifies the measurement.",
|
|
142724
|
-
"schema": { "type": "string" }
|
|
142725
|
-
},
|
|
142726
|
-
{
|
|
142727
|
-
"name": "series",
|
|
142728
|
-
"in": "query",
|
|
142729
|
-
"description": "The specific series to search for.",
|
|
142730
|
-
"schema": { "type": "string" }
|
|
142731
|
-
}
|
|
142732
|
-
],
|
|
142733
|
-
"responses": { "200": {
|
|
142734
|
-
"description": "OK",
|
|
142735
|
-
"content": { "application/json": { "schema": { "type": "integer" } } }
|
|
143396
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedDeviceList" } } }
|
|
142736
143397
|
} }
|
|
142737
143398
|
} },
|
|
142738
|
-
"/service/dtm/assets/
|
|
143399
|
+
"/service/dtm/assets/subAssets": { "get": {
|
|
142739
143400
|
"tags": ["Assets"],
|
|
142740
|
-
"summary": "Retrieve
|
|
142741
|
-
"description": "Retrieves
|
|
142742
|
-
"operationId": "
|
|
143401
|
+
"summary": "Retrieve sub-assets by parent IDs",
|
|
143402
|
+
"description": "Retrieves sub-assets across multiple parent assets.\n\n Fetches all direct sub-assets that are children of given parent asset IDs. ",
|
|
143403
|
+
"operationId": "getSubAssetsByParents",
|
|
142743
143404
|
"parameters": [
|
|
142744
143405
|
{
|
|
142745
|
-
"name": "
|
|
142746
|
-
"in": "
|
|
143406
|
+
"name": "parents",
|
|
143407
|
+
"in": "query",
|
|
143408
|
+
"description": "List of parent asset IDs used to scope the sub-assets query. At least one parent ID must be provided. Sub-assets that are direct children of any of the given parent assets are returned.",
|
|
142747
143409
|
"required": true,
|
|
142748
|
-
"
|
|
143410
|
+
"explode": false,
|
|
143411
|
+
"schema": {
|
|
143412
|
+
"minItems": 1,
|
|
143413
|
+
"type": "array",
|
|
143414
|
+
"items": { "type": "string" }
|
|
143415
|
+
}
|
|
142749
143416
|
},
|
|
142750
143417
|
{
|
|
142751
143418
|
"name": "query",
|
|
@@ -142770,13 +143437,48 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142770
143437
|
}
|
|
142771
143438
|
}
|
|
142772
143439
|
},
|
|
143440
|
+
{
|
|
143441
|
+
"name": "withLinkedSeries",
|
|
143442
|
+
"in": "query",
|
|
143443
|
+
"description": "Indicates the resource to add the `c8y_LinkedSeries` fragment in the response.",
|
|
143444
|
+
"schema": {
|
|
143445
|
+
"type": "boolean",
|
|
143446
|
+
"default": false
|
|
143447
|
+
}
|
|
143448
|
+
},
|
|
143449
|
+
{
|
|
143450
|
+
"name": "withParents",
|
|
143451
|
+
"in": "query",
|
|
143452
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
143453
|
+
"schema": {
|
|
143454
|
+
"type": "boolean",
|
|
143455
|
+
"default": false
|
|
143456
|
+
}
|
|
143457
|
+
},
|
|
143458
|
+
{
|
|
143459
|
+
"name": "includeGroups",
|
|
143460
|
+
"in": "query",
|
|
143461
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
143462
|
+
"schema": {
|
|
143463
|
+
"type": "boolean",
|
|
143464
|
+
"default": false
|
|
143465
|
+
}
|
|
143466
|
+
},
|
|
143467
|
+
{
|
|
143468
|
+
"name": "withChildrenCount",
|
|
143469
|
+
"in": "query",
|
|
143470
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
143471
|
+
"schema": {
|
|
143472
|
+
"type": "boolean",
|
|
143473
|
+
"default": false
|
|
143474
|
+
}
|
|
143475
|
+
},
|
|
142773
143476
|
{
|
|
142774
143477
|
"name": "currentPage",
|
|
142775
143478
|
"in": "query",
|
|
142776
143479
|
"schema": {
|
|
142777
|
-
"
|
|
142778
|
-
"
|
|
142779
|
-
"default": 1
|
|
143480
|
+
"minimum": 1,
|
|
143481
|
+
"type": "integer"
|
|
142780
143482
|
}
|
|
142781
143483
|
},
|
|
142782
143484
|
{
|
|
@@ -142791,13 +143493,13 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142791
143493
|
],
|
|
142792
143494
|
"responses": { "200": {
|
|
142793
143495
|
"description": "OK",
|
|
142794
|
-
"content": { "application/json": { "schema": { "
|
|
143496
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedAssetList" } } }
|
|
142795
143497
|
} }
|
|
142796
143498
|
} },
|
|
142797
143499
|
"/service/dtm/assets/linkedSeries": { "get": {
|
|
142798
143500
|
"tags": ["Linked Series"],
|
|
142799
143501
|
"summary": "Retrieve linked series for all assets",
|
|
142800
|
-
"description": "Retrieves linked series for all assets on your tenant.\n\n Provides a list of linked series, with a set of optional query parameters to filter for specific linked series, asset types or asset ids.",
|
|
143502
|
+
"description": "Retrieves linked series for all assets on your tenant.\n\n Provides a list of linked series, with a set of optional query parameters to filter for specific linked series, asset types or asset ids. ",
|
|
142801
143503
|
"operationId": "getLinkedSeriesByQuery",
|
|
142802
143504
|
"parameters": [
|
|
142803
143505
|
{
|
|
@@ -142832,9 +143534,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142832
143534
|
"name": "currentPage",
|
|
142833
143535
|
"in": "query",
|
|
142834
143536
|
"schema": {
|
|
142835
|
-
"
|
|
142836
|
-
"
|
|
142837
|
-
"default": 1
|
|
143537
|
+
"minimum": 1,
|
|
143538
|
+
"type": "integer"
|
|
142838
143539
|
}
|
|
142839
143540
|
},
|
|
142840
143541
|
{
|
|
@@ -142852,15 +143553,66 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142852
143553
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedAssetList" } } }
|
|
142853
143554
|
} }
|
|
142854
143555
|
} },
|
|
143556
|
+
"/service/dtm/assets/linkedSeries/opposites/{deviceId}": { "get": {
|
|
143557
|
+
"tags": ["Linked Series"],
|
|
143558
|
+
"summary": "Get all assets linked to a specific device by its ID.",
|
|
143559
|
+
"description": "The API response with the default `Accept` header `application/json` is deprecated and will be removed in a future\nversion. Please use the `Accept` header `application/vnd.com.nsn.cumulocity.linkedassetscollection+json` which offers\nbetter performance and filter parameters.",
|
|
143560
|
+
"operationId": "getOppositeAssets",
|
|
143561
|
+
"parameters": [
|
|
143562
|
+
{
|
|
143563
|
+
"name": "deviceId",
|
|
143564
|
+
"in": "path",
|
|
143565
|
+
"description": "the ID of the device",
|
|
143566
|
+
"required": true,
|
|
143567
|
+
"schema": { "type": "string" }
|
|
143568
|
+
},
|
|
143569
|
+
{
|
|
143570
|
+
"name": "assetIds",
|
|
143571
|
+
"in": "query",
|
|
143572
|
+
"description": "The asset IDs to search for.",
|
|
143573
|
+
"explode": false,
|
|
143574
|
+
"schema": {
|
|
143575
|
+
"type": "array",
|
|
143576
|
+
"items": { "type": "string" }
|
|
143577
|
+
}
|
|
143578
|
+
},
|
|
143579
|
+
{
|
|
143580
|
+
"name": "fragment",
|
|
143581
|
+
"in": "query",
|
|
143582
|
+
"description": "A characteristic which identifies the measurement.",
|
|
143583
|
+
"schema": { "type": "string" }
|
|
143584
|
+
},
|
|
143585
|
+
{
|
|
143586
|
+
"name": "series",
|
|
143587
|
+
"in": "query",
|
|
143588
|
+
"description": "The specific series to search for.",
|
|
143589
|
+
"schema": { "type": "string" }
|
|
143590
|
+
}
|
|
143591
|
+
],
|
|
143592
|
+
"responses": { "200": {
|
|
143593
|
+
"description": "a set of assets linked to the specified device",
|
|
143594
|
+
"content": {
|
|
143595
|
+
"application/vnd.com.nsn.cumulocity.linkedassetscollection+json": { "schema": {
|
|
143596
|
+
"type": "array",
|
|
143597
|
+
"items": { "$ref": "#/components/schemas/LinkedAsset" }
|
|
143598
|
+
} },
|
|
143599
|
+
"application/json": { "schema": {
|
|
143600
|
+
"type": "array",
|
|
143601
|
+
"items": { "$ref": "#/components/schemas/Asset" }
|
|
143602
|
+
} }
|
|
143603
|
+
}
|
|
143604
|
+
} }
|
|
143605
|
+
} },
|
|
142855
143606
|
"/service/dtm/assets/externalIds/{externalId}": { "get": {
|
|
142856
143607
|
"tags": ["Assets"],
|
|
142857
143608
|
"summary": "Retrieve an asset by its external ID",
|
|
142858
|
-
"description": "Retrieves an Asset by its external ID of type `c8y_ExternalId`.\n\n This endpoint allows clients to look up assets using identifiers from external systems. It resolves the external ID to a ManagedObject and returns the corresponding Asset if found.",
|
|
143609
|
+
"description": "Retrieves an Asset by its external ID of type `c8y_ExternalId`.\n\n This endpoint allows clients to look up assets using identifiers from external systems. It resolves the external ID to a ManagedObject and returns the corresponding Asset if found. ",
|
|
142859
143610
|
"operationId": "getAssetByExternalId",
|
|
142860
143611
|
"parameters": [
|
|
142861
143612
|
{
|
|
142862
143613
|
"name": "externalId",
|
|
142863
143614
|
"in": "path",
|
|
143615
|
+
"description": "The external identifier of type `c8y_Asset` of the asset.",
|
|
142864
143616
|
"required": true,
|
|
142865
143617
|
"schema": { "type": "string" }
|
|
142866
143618
|
},
|
|
@@ -142876,7 +143628,26 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142876
143628
|
{
|
|
142877
143629
|
"name": "withSubAssets",
|
|
142878
143630
|
"in": "query",
|
|
142879
|
-
"description": "Indicates the resource to add the sub-assets in the response.",
|
|
143631
|
+
"description": "**Deprecated** – use the dedicated `/assets/{assetId}/subAssets`, `/assets/externalIds/{externalId}/subAssets` or `/assets/subAssets` endpoint instead!<br>Indicates the resource to add the sub-assets in the response.",
|
|
143632
|
+
"deprecated": true,
|
|
143633
|
+
"schema": {
|
|
143634
|
+
"type": "boolean",
|
|
143635
|
+
"default": false
|
|
143636
|
+
}
|
|
143637
|
+
},
|
|
143638
|
+
{
|
|
143639
|
+
"name": "withParents",
|
|
143640
|
+
"in": "query",
|
|
143641
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
143642
|
+
"schema": {
|
|
143643
|
+
"type": "boolean",
|
|
143644
|
+
"default": false
|
|
143645
|
+
}
|
|
143646
|
+
},
|
|
143647
|
+
{
|
|
143648
|
+
"name": "withChildrenCount",
|
|
143649
|
+
"in": "query",
|
|
143650
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
142880
143651
|
"schema": {
|
|
142881
143652
|
"type": "boolean",
|
|
142882
143653
|
"default": false
|
|
@@ -142888,41 +143659,148 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142888
143659
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Asset" } } }
|
|
142889
143660
|
} }
|
|
142890
143661
|
} },
|
|
143662
|
+
"/service/dtm/assets/externalIds/{externalId}/subAssets": { "get": {
|
|
143663
|
+
"tags": ["Assets"],
|
|
143664
|
+
"summary": "Retrieve sub-assets of an asset by its external ID",
|
|
143665
|
+
"description": "Retrieves direct sub-assets of the asset identified by the given external ID.\n\n Resolves the asset by its `c8y_ExternalId` and returns a paginated list of its immediate child assets. ",
|
|
143666
|
+
"operationId": "getSubAssetsByExternalId",
|
|
143667
|
+
"parameters": [
|
|
143668
|
+
{
|
|
143669
|
+
"name": "externalId",
|
|
143670
|
+
"in": "path",
|
|
143671
|
+
"description": "The external identifier of type `c8y_Asset` of the asset.",
|
|
143672
|
+
"required": true,
|
|
143673
|
+
"schema": { "type": "string" }
|
|
143674
|
+
},
|
|
143675
|
+
{
|
|
143676
|
+
"name": "query",
|
|
143677
|
+
"in": "query",
|
|
143678
|
+
"description": "Use the `$filter` keyword to specify filtering criteria. Filtering can be applied to properties of the asset. Detailed information can be found within the Cumulocity core OpenAPI [here](https://cumulocity.com/api/core/#tag/Query-language).",
|
|
143679
|
+
"schema": {
|
|
143680
|
+
"type": "string",
|
|
143681
|
+
"format": "c8y:query"
|
|
143682
|
+
},
|
|
143683
|
+
"examples": {
|
|
143684
|
+
"Filter by name": {
|
|
143685
|
+
"description": "Filter by name",
|
|
143686
|
+
"value": "$filter=name eq 'Windfarm'"
|
|
143687
|
+
},
|
|
143688
|
+
"Filter by type": {
|
|
143689
|
+
"description": "Filter by type",
|
|
143690
|
+
"value": "$filter=type eq 'c8y_*'"
|
|
143691
|
+
},
|
|
143692
|
+
"Using orderby": {
|
|
143693
|
+
"description": "Using orderby",
|
|
143694
|
+
"value": "$orderby=id asc"
|
|
143695
|
+
}
|
|
143696
|
+
}
|
|
143697
|
+
},
|
|
143698
|
+
{
|
|
143699
|
+
"name": "withLinkedSeries",
|
|
143700
|
+
"in": "query",
|
|
143701
|
+
"description": "Indicates the resource to add the `c8y_LinkedSeries` fragment in the response.",
|
|
143702
|
+
"schema": {
|
|
143703
|
+
"type": "boolean",
|
|
143704
|
+
"default": false
|
|
143705
|
+
}
|
|
143706
|
+
},
|
|
143707
|
+
{
|
|
143708
|
+
"name": "withParents",
|
|
143709
|
+
"in": "query",
|
|
143710
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
143711
|
+
"schema": {
|
|
143712
|
+
"type": "boolean",
|
|
143713
|
+
"default": false
|
|
143714
|
+
}
|
|
143715
|
+
},
|
|
143716
|
+
{
|
|
143717
|
+
"name": "includeGroups",
|
|
143718
|
+
"in": "query",
|
|
143719
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
143720
|
+
"schema": {
|
|
143721
|
+
"type": "boolean",
|
|
143722
|
+
"default": false
|
|
143723
|
+
}
|
|
143724
|
+
},
|
|
143725
|
+
{
|
|
143726
|
+
"name": "withChildrenCount",
|
|
143727
|
+
"in": "query",
|
|
143728
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
143729
|
+
"schema": {
|
|
143730
|
+
"type": "boolean",
|
|
143731
|
+
"default": false
|
|
143732
|
+
}
|
|
143733
|
+
},
|
|
143734
|
+
{
|
|
143735
|
+
"name": "currentPage",
|
|
143736
|
+
"in": "query",
|
|
143737
|
+
"schema": {
|
|
143738
|
+
"minimum": 1,
|
|
143739
|
+
"type": "integer"
|
|
143740
|
+
}
|
|
143741
|
+
},
|
|
143742
|
+
{
|
|
143743
|
+
"name": "pageSize",
|
|
143744
|
+
"in": "query",
|
|
143745
|
+
"schema": {
|
|
143746
|
+
"maximum": 2e3,
|
|
143747
|
+
"minimum": 1,
|
|
143748
|
+
"type": "integer"
|
|
143749
|
+
}
|
|
143750
|
+
}
|
|
143751
|
+
],
|
|
143752
|
+
"responses": { "200": {
|
|
143753
|
+
"description": "OK",
|
|
143754
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedAssetList" } } }
|
|
143755
|
+
} }
|
|
143756
|
+
} },
|
|
142891
143757
|
"/service/dtm/assets/count": { "get": {
|
|
142892
143758
|
"tags": ["Assets"],
|
|
142893
143759
|
"summary": "Get count of assets",
|
|
142894
|
-
"description": "Counts all assets on your tenant.\n\n You can optionally apply the filters `query` and `
|
|
143760
|
+
"description": "Counts all assets on your tenant.\n\n You can optionally apply the filters `query`, `onlyRoots` and `includeGroups`. ",
|
|
142895
143761
|
"operationId": "countAssets",
|
|
142896
|
-
"parameters": [
|
|
142897
|
-
|
|
142898
|
-
|
|
142899
|
-
|
|
142900
|
-
|
|
142901
|
-
"
|
|
142902
|
-
|
|
142903
|
-
|
|
142904
|
-
"examples": {
|
|
142905
|
-
"Filter by name": {
|
|
142906
|
-
"description": "Filter by name",
|
|
142907
|
-
"value": "$filter=name eq 'Windfarm'"
|
|
142908
|
-
},
|
|
142909
|
-
"Filter by type": {
|
|
142910
|
-
"description": "Filter by type",
|
|
142911
|
-
"value": "$filter=type eq 'c8y_*'"
|
|
143762
|
+
"parameters": [
|
|
143763
|
+
{
|
|
143764
|
+
"name": "query",
|
|
143765
|
+
"in": "query",
|
|
143766
|
+
"description": "Use the `$filter` keyword to specify filtering criteria. Filtering can be applied to properties of the asset. Detailed information can be found within the Cumulocity core OpenAPI [here](https://cumulocity.com/api/core/#tag/Query-language).",
|
|
143767
|
+
"schema": {
|
|
143768
|
+
"type": "string",
|
|
143769
|
+
"format": "c8y:query"
|
|
142912
143770
|
},
|
|
142913
|
-
"
|
|
142914
|
-
"
|
|
142915
|
-
|
|
143771
|
+
"examples": {
|
|
143772
|
+
"Filter by name": {
|
|
143773
|
+
"description": "Filter by name",
|
|
143774
|
+
"value": "$filter=name eq 'Windfarm'"
|
|
143775
|
+
},
|
|
143776
|
+
"Filter by type": {
|
|
143777
|
+
"description": "Filter by type",
|
|
143778
|
+
"value": "$filter=type eq 'c8y_*'"
|
|
143779
|
+
},
|
|
143780
|
+
"Using orderby": {
|
|
143781
|
+
"description": "Using orderby",
|
|
143782
|
+
"value": "$orderby=id asc"
|
|
143783
|
+
}
|
|
143784
|
+
}
|
|
143785
|
+
},
|
|
143786
|
+
{
|
|
143787
|
+
"name": "onlyRoots",
|
|
143788
|
+
"in": "query",
|
|
143789
|
+
"schema": {
|
|
143790
|
+
"type": "boolean",
|
|
143791
|
+
"default": false
|
|
143792
|
+
}
|
|
143793
|
+
},
|
|
143794
|
+
{
|
|
143795
|
+
"name": "includeGroups",
|
|
143796
|
+
"in": "query",
|
|
143797
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
143798
|
+
"schema": {
|
|
143799
|
+
"type": "boolean",
|
|
143800
|
+
"default": false
|
|
142916
143801
|
}
|
|
142917
143802
|
}
|
|
142918
|
-
|
|
142919
|
-
"name": "onlyRoots",
|
|
142920
|
-
"in": "query",
|
|
142921
|
-
"schema": {
|
|
142922
|
-
"type": "boolean",
|
|
142923
|
-
"default": false
|
|
142924
|
-
}
|
|
142925
|
-
}],
|
|
143803
|
+
],
|
|
142926
143804
|
"responses": { "200": {
|
|
142927
143805
|
"description": "OK",
|
|
142928
143806
|
"content": { "application/json": { "schema": { "type": "integer" } } }
|
|
@@ -142949,7 +143827,11 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142949
143827
|
"pattern": "^(?:[A-Za-z0-9](?:\\s*[-\\w:.!])*\\s*)?$",
|
|
142950
143828
|
"type": "string"
|
|
142951
143829
|
},
|
|
142952
|
-
"description": { "type": "string" }
|
|
143830
|
+
"description": { "type": "string" },
|
|
143831
|
+
"additionalProperties": {
|
|
143832
|
+
"type": "boolean",
|
|
143833
|
+
"writeOnly": true
|
|
143834
|
+
}
|
|
142953
143835
|
},
|
|
142954
143836
|
"example": {
|
|
142955
143837
|
"title": "Position",
|
|
@@ -143010,12 +143892,20 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143010
143892
|
}
|
|
143011
143893
|
},
|
|
143012
143894
|
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
143895
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" },
|
|
143013
143896
|
"c8y_Origin": {
|
|
143014
143897
|
"type": "string",
|
|
143015
143898
|
"description": "Specifies the origin of this Property Definition and identifies the owning component. If the value is <code>library</code>, the Property Definition is system-defined and read-only."
|
|
143016
143899
|
}
|
|
143017
143900
|
}
|
|
143018
143901
|
},
|
|
143902
|
+
"SharedDefinition": {
|
|
143903
|
+
"type": "object",
|
|
143904
|
+
"properties": { "self": { "type": "string" } },
|
|
143905
|
+
"additionalProperties": false,
|
|
143906
|
+
"description": "Indicates that this definition is maintained on the enterprise tenant and is shared on its subtenants.",
|
|
143907
|
+
"readOnly": true
|
|
143908
|
+
},
|
|
143019
143909
|
"AllowedPropertyDefinition": {
|
|
143020
143910
|
"required": ["identifier"],
|
|
143021
143911
|
"type": "object",
|
|
@@ -143072,7 +143962,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143072
143962
|
} },
|
|
143073
143963
|
"additionalProperties": false
|
|
143074
143964
|
},
|
|
143075
|
-
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" }
|
|
143965
|
+
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
143966
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" }
|
|
143076
143967
|
}
|
|
143077
143968
|
},
|
|
143078
143969
|
"EventDefinition": {
|
|
@@ -143105,6 +143996,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143105
143996
|
}
|
|
143106
143997
|
},
|
|
143107
143998
|
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
143999
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" },
|
|
143108
144000
|
"composition": {
|
|
143109
144001
|
"type": "object",
|
|
143110
144002
|
"properties": { "allowedProperties": {
|
|
@@ -143146,6 +144038,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143146
144038
|
}
|
|
143147
144039
|
},
|
|
143148
144040
|
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
144041
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" },
|
|
143149
144042
|
"composition": {
|
|
143150
144043
|
"type": "object",
|
|
143151
144044
|
"properties": {
|
|
@@ -143202,6 +144095,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143202
144095
|
}
|
|
143203
144096
|
},
|
|
143204
144097
|
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
144098
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" },
|
|
143205
144099
|
"composition": {
|
|
143206
144100
|
"type": "object",
|
|
143207
144101
|
"properties": { "allowedProperties": {
|
|
@@ -143246,6 +144140,11 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143246
144140
|
"readOnly": true,
|
|
143247
144141
|
"items": { "$ref": "#/components/schemas/Asset" }
|
|
143248
144142
|
},
|
|
144143
|
+
"assetParents": {
|
|
144144
|
+
"type": "array",
|
|
144145
|
+
"readOnly": true,
|
|
144146
|
+
"items": { "$ref": "#/components/schemas/Asset" }
|
|
144147
|
+
},
|
|
143249
144148
|
"c8y_LinkedSeries": {
|
|
143250
144149
|
"type": "array",
|
|
143251
144150
|
"items": { "$ref": "#/components/schemas/LinkedSeries" }
|
|
@@ -143256,7 +144155,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143256
144155
|
},
|
|
143257
144156
|
"c8y_LatestMeasurements": { "$ref": "#/components/schemas/LatestMeasurements" },
|
|
143258
144157
|
"c8y_ExternalId": {
|
|
143259
|
-
"maxLength":
|
|
144158
|
+
"maxLength": 680,
|
|
143260
144159
|
"minLength": 1,
|
|
143261
144160
|
"type": "string",
|
|
143262
144161
|
"description": "Represents an external identifier used to reference this asset in external systems. When set, an identity of type 'c8y_Asset' is created for this asset with the value of this property."
|
|
@@ -143264,6 +144163,11 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143264
144163
|
"c8y_ExternalAsset": {
|
|
143265
144164
|
"type": "object",
|
|
143266
144165
|
"description": "Represents Assets synchronized from external systems into Cumulocity IoT. The fragment allows storing free-form metadata relevant to external systems."
|
|
144166
|
+
},
|
|
144167
|
+
"assignedChildrenCount": {
|
|
144168
|
+
"type": "integer",
|
|
144169
|
+
"format": "int32",
|
|
144170
|
+
"readOnly": true
|
|
143267
144171
|
}
|
|
143268
144172
|
}
|
|
143269
144173
|
},
|
|
@@ -143343,6 +144247,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143343
144247
|
"example": "T"
|
|
143344
144248
|
},
|
|
143345
144249
|
"id": {
|
|
144250
|
+
"pattern": "^(?!\\s*$).+$",
|
|
143346
144251
|
"type": "string",
|
|
143347
144252
|
"example": "9688123"
|
|
143348
144253
|
},
|
|
@@ -143357,6 +144262,51 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143357
144262
|
},
|
|
143358
144263
|
"additionalProperties": false
|
|
143359
144264
|
},
|
|
144265
|
+
"AssetReference": {
|
|
144266
|
+
"type": "object",
|
|
144267
|
+
"properties": {
|
|
144268
|
+
"fragment": {
|
|
144269
|
+
"type": "string",
|
|
144270
|
+
"example": "c8y_Temperature"
|
|
144271
|
+
},
|
|
144272
|
+
"series": {
|
|
144273
|
+
"type": "string",
|
|
144274
|
+
"example": "T"
|
|
144275
|
+
},
|
|
144276
|
+
"id": {
|
|
144277
|
+
"type": "string",
|
|
144278
|
+
"example": "9688123"
|
|
144279
|
+
},
|
|
144280
|
+
"label": {
|
|
144281
|
+
"type": "string",
|
|
144282
|
+
"example": "Room temperature"
|
|
144283
|
+
}
|
|
144284
|
+
},
|
|
144285
|
+
"additionalProperties": false
|
|
144286
|
+
},
|
|
144287
|
+
"LinkedAsset": {
|
|
144288
|
+
"type": "object",
|
|
144289
|
+
"properties": {
|
|
144290
|
+
"fragment": {
|
|
144291
|
+
"type": "string",
|
|
144292
|
+
"example": "c8y_Temperature"
|
|
144293
|
+
},
|
|
144294
|
+
"series": {
|
|
144295
|
+
"type": "string",
|
|
144296
|
+
"example": "T"
|
|
144297
|
+
},
|
|
144298
|
+
"type": {
|
|
144299
|
+
"type": "string",
|
|
144300
|
+
"example": "c8y_TemperatureMeasurement"
|
|
144301
|
+
},
|
|
144302
|
+
"name": {
|
|
144303
|
+
"type": "string",
|
|
144304
|
+
"example": "MyTemperatureMeasurement"
|
|
144305
|
+
},
|
|
144306
|
+
"asset": { "$ref": "#/components/schemas/AssetReference" }
|
|
144307
|
+
},
|
|
144308
|
+
"additionalProperties": false
|
|
144309
|
+
},
|
|
143360
144310
|
"PageStatistics": {
|
|
143361
144311
|
"required": [
|
|
143362
144312
|
"currentPage",
|
|
@@ -143392,7 +144342,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143392
144342
|
"additionalProperties": false
|
|
143393
144343
|
},
|
|
143394
144344
|
"PaginatedDefinitionList": {
|
|
143395
|
-
"required": ["
|
|
144345
|
+
"required": ["statistics"],
|
|
143396
144346
|
"type": "object",
|
|
143397
144347
|
"properties": {
|
|
143398
144348
|
"self": {
|
|
@@ -143497,6 +144447,71 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143497
144447
|
},
|
|
143498
144448
|
"additionalProperties": false
|
|
143499
144449
|
},
|
|
144450
|
+
"Device": {
|
|
144451
|
+
"title": "Device",
|
|
144452
|
+
"required": ["id"],
|
|
144453
|
+
"type": "object",
|
|
144454
|
+
"properties": {
|
|
144455
|
+
"creationTime": {
|
|
144456
|
+
"type": "string",
|
|
144457
|
+
"readOnly": true,
|
|
144458
|
+
"example": "2017-12-12T22:09:06.881+01:00"
|
|
144459
|
+
},
|
|
144460
|
+
"lastUpdated": {
|
|
144461
|
+
"type": "string",
|
|
144462
|
+
"readOnly": true,
|
|
144463
|
+
"example": "2018-07-19T12:01:50.731Z"
|
|
144464
|
+
},
|
|
144465
|
+
"id": {
|
|
144466
|
+
"type": "string",
|
|
144467
|
+
"readOnly": true,
|
|
144468
|
+
"example": "4512412"
|
|
144469
|
+
},
|
|
144470
|
+
"type": {
|
|
144471
|
+
"type": "string",
|
|
144472
|
+
"example": "c8y_TemperatureSensor"
|
|
144473
|
+
},
|
|
144474
|
+
"name": {
|
|
144475
|
+
"type": "string",
|
|
144476
|
+
"example": "TemperatureSensor"
|
|
144477
|
+
},
|
|
144478
|
+
"owner": { "type": "string" },
|
|
144479
|
+
"c8y_LatestMeasurements": { "$ref": "#/components/schemas/LatestMeasurements" }
|
|
144480
|
+
}
|
|
144481
|
+
},
|
|
144482
|
+
"PaginatedDeviceList": {
|
|
144483
|
+
"required": ["devices", "statistics"],
|
|
144484
|
+
"type": "object",
|
|
144485
|
+
"properties": {
|
|
144486
|
+
"self": {
|
|
144487
|
+
"title": "SelfURL",
|
|
144488
|
+
"type": "string",
|
|
144489
|
+
"description": "A URL linking to this resource.",
|
|
144490
|
+
"format": "uri",
|
|
144491
|
+
"readOnly": true
|
|
144492
|
+
},
|
|
144493
|
+
"next": {
|
|
144494
|
+
"title": "NextPageURL",
|
|
144495
|
+
"type": "string",
|
|
144496
|
+
"description": "A URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] to a potential next page.",
|
|
144497
|
+
"format": "uri",
|
|
144498
|
+
"readOnly": true
|
|
144499
|
+
},
|
|
144500
|
+
"prev": {
|
|
144501
|
+
"title": "PreviousPageURL",
|
|
144502
|
+
"type": "string",
|
|
144503
|
+
"description": "A URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] to a potential previous page.",
|
|
144504
|
+
"format": "uri",
|
|
144505
|
+
"readOnly": true
|
|
144506
|
+
},
|
|
144507
|
+
"devices": {
|
|
144508
|
+
"type": "array",
|
|
144509
|
+
"items": { "$ref": "#/components/schemas/Device" }
|
|
144510
|
+
},
|
|
144511
|
+
"statistics": { "$ref": "#/components/schemas/PageStatistics" }
|
|
144512
|
+
},
|
|
144513
|
+
"additionalProperties": false
|
|
144514
|
+
},
|
|
143500
144515
|
"AllowedSubAssetDefinition": {
|
|
143501
144516
|
"required": ["identifier"],
|
|
143502
144517
|
"type": "object",
|
|
@@ -143898,6 +144913,10 @@ runMain(defineCommand({
|
|
|
143898
144913
|
"no-mcp": {
|
|
143899
144914
|
type: "string",
|
|
143900
144915
|
description: "Disable MCP wrapping: pass \"*\" (or no value) for all services, or a contextPath. Can be repeated. Opted-out services fall back to their OpenAPI spec."
|
|
144916
|
+
},
|
|
144917
|
+
"mcp-server": {
|
|
144918
|
+
type: "string",
|
|
144919
|
+
description: "External MCP server to expose as a codemode namespace, as JSON: '{\"name\":\"github\",\"url\":\"https://api.githubcopilot.com/mcp/\",\"token\":\"…\"}'. Can be repeated."
|
|
143901
144920
|
}
|
|
143902
144921
|
},
|
|
143903
144922
|
setup: () => {
|
|
@@ -143922,6 +144941,10 @@ runMain(defineCommand({
|
|
|
143922
144941
|
const rawNoMcp = args["no-mcp"];
|
|
143923
144942
|
const noMcp = parseNoMcp(Array.isArray(rawNoMcp) ? rawNoMcp : rawNoMcp !== void 0 ? [rawNoMcp] : []);
|
|
143924
144943
|
if (noMcp.all || noMcp.contextPaths.size > 0) consola.info(`MCP wrapping disabled for: ${noMcp.all ? "all services" : [...noMcp.contextPaths].join(", ")}`);
|
|
144944
|
+
const rawMcpServers = args["mcp-server"];
|
|
144945
|
+
const { servers: externalMcpServers, failedEntries: failedMcpServers } = parseExternalMcpServers((Array.isArray(rawMcpServers) ? rawMcpServers : rawMcpServers ? [rawMcpServers] : []).filter((v) => typeof v === "string" && v.length > 0));
|
|
144946
|
+
if (failedMcpServers.length > 0) throw new Error(["One or more --mcp-server flags could not be parsed:", ...failedMcpServers.map((e) => `- ${e.entry}: ${e.reason}`)].join("\n"));
|
|
144947
|
+
if (externalMcpServers.length > 0) consola.info(`External MCP namespaces: ${externalMcpServers.map((s) => `${s.name} (${s.url})`).join(", ")}`);
|
|
143925
144948
|
const activeTenant = readActiveTenantUrl();
|
|
143926
144949
|
if (activeTenant) try {
|
|
143927
144950
|
const tenantCtx = await setCliTenantContext(activeTenant);
|
|
@@ -143946,6 +144969,7 @@ runMain(defineCommand({
|
|
|
143946
144969
|
restrictions,
|
|
143947
144970
|
allowRules: parsedAllowRules,
|
|
143948
144971
|
noMcp,
|
|
144972
|
+
externalMcpServers,
|
|
143949
144973
|
specs: active?.specs ?? getBundledOnlyCapabilities(),
|
|
143950
144974
|
auth: active ? {
|
|
143951
144975
|
tenantUrl: active.tenantUrl,
|