mc8yp 2.6.2 → 2.7.0
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 +61 -1
- package/dist/cli.mjs +1429 -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.0";
|
|
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,394 @@ 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 = validateEntry(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
|
+
function validateEntry(candidate, usedNames) {
|
|
78281
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) return { reason: "Entry must be a JSON object with \"name\" and \"url\"." };
|
|
78282
|
+
const entry = candidate;
|
|
78283
|
+
if (typeof entry.name !== "string" || entry.name === "") return { reason: "\"name\" is required and must be a non-empty string — it becomes the sandbox namespace." };
|
|
78284
|
+
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}".` };
|
|
78285
|
+
if (RESERVED_NAMESPACES.has(entry.name)) return { reason: `"name" must not be a reserved namespace (${[...RESERVED_NAMESPACES].join(", ")}), got "${entry.name}".` };
|
|
78286
|
+
if (usedNames.has(entry.name)) return { reason: `Duplicate namespace "${entry.name}" — each external MCP server needs its own name.` };
|
|
78287
|
+
if (typeof entry.url !== "string" || entry.url === "") return { reason: `"url" is required and must be a non-empty string (server "${entry.name}").` };
|
|
78288
|
+
let url;
|
|
78289
|
+
try {
|
|
78290
|
+
url = new URL(entry.url);
|
|
78291
|
+
} catch {
|
|
78292
|
+
return { reason: `"url" must be an absolute URL, got "${entry.url}" (server "${entry.name}").` };
|
|
78293
|
+
}
|
|
78294
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return { reason: `"url" must use http or https, got "${url.protocol}" (server "${entry.name}").` };
|
|
78295
|
+
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}").` };
|
|
78296
|
+
if (entry.description !== void 0 && typeof entry.description !== "string") return { reason: `"description" must be a string when present (server "${entry.name}").` };
|
|
78297
|
+
let headers;
|
|
78298
|
+
if (entry.headers !== void 0) {
|
|
78299
|
+
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}").` };
|
|
78300
|
+
headers = {};
|
|
78301
|
+
for (const [key, value] of Object.entries(entry.headers)) {
|
|
78302
|
+
if (typeof value !== "string") return { reason: `"headers.${key}" must be a string (server "${entry.name}").` };
|
|
78303
|
+
headers[key] = value;
|
|
78304
|
+
}
|
|
78305
|
+
}
|
|
78306
|
+
return { config: {
|
|
78307
|
+
name: entry.name,
|
|
78308
|
+
url: entry.url,
|
|
78309
|
+
...typeof entry.token === "string" ? { token: entry.token } : {},
|
|
78310
|
+
...headers ? { headers } : {},
|
|
78311
|
+
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
78312
|
+
} };
|
|
78313
|
+
}
|
|
78314
|
+
/**
|
|
78315
|
+
* Request headers for one external server: the `token` bearer shorthand,
|
|
78316
|
+
* then the explicit `headers` map so it can override.
|
|
78317
|
+
* @param config External server config.
|
|
78318
|
+
*/
|
|
78319
|
+
function externalMcpHeaders(config) {
|
|
78320
|
+
return {
|
|
78321
|
+
...config.token ? { authorization: `Bearer ${config.token}` } : {},
|
|
78322
|
+
...config.headers
|
|
78323
|
+
};
|
|
78324
|
+
}
|
|
78325
|
+
/**
|
|
78326
|
+
* Transport for an external MCP server: plain `fetch` against the absolute
|
|
78327
|
+
* URL with the entry's own credentials attached. No tenant auth, no safeFetch
|
|
78328
|
+
* host pinning — the target is an arbitrary operator-chosen host, not the
|
|
78329
|
+
* tenant.
|
|
78330
|
+
* @param config External server config.
|
|
78331
|
+
*/
|
|
78332
|
+
function createExternalMcpFetch(config) {
|
|
78333
|
+
const authHeaders = externalMcpHeaders(config);
|
|
78334
|
+
return (url, init) => fetch(url, {
|
|
78335
|
+
...init,
|
|
78336
|
+
headers: {
|
|
78337
|
+
...init.headers,
|
|
78338
|
+
...authHeaders
|
|
78339
|
+
}
|
|
78340
|
+
});
|
|
78341
|
+
}
|
|
78342
|
+
//#endregion
|
|
78343
|
+
//#region src/codemode/external-mcp-session.ts
|
|
78344
|
+
const IDLE_TTL_MS = 900 * 1e3;
|
|
78345
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
78346
|
+
function configKey(config) {
|
|
78347
|
+
return JSON.stringify([
|
|
78348
|
+
config.name,
|
|
78349
|
+
config.url,
|
|
78350
|
+
config.token ?? "",
|
|
78351
|
+
config.headers ?? {}
|
|
78352
|
+
]);
|
|
78353
|
+
}
|
|
78354
|
+
function armIdleTimer(sessionKey) {
|
|
78355
|
+
const session = sessions.get(sessionKey);
|
|
78356
|
+
if (!session) return;
|
|
78357
|
+
if (session.timer) clearTimeout(session.timer);
|
|
78358
|
+
session.timer = setTimeout(() => evictExternalMcpSession(sessionKey, "idle-timeout"), IDLE_TTL_MS);
|
|
78359
|
+
session.timer.unref?.();
|
|
78360
|
+
}
|
|
78361
|
+
async function listExternalTools(config) {
|
|
78362
|
+
const client = new McpHttpClient({
|
|
78363
|
+
url: config.url,
|
|
78364
|
+
fetch: createExternalMcpFetch(config)
|
|
78365
|
+
});
|
|
78366
|
+
try {
|
|
78367
|
+
const info = await client.initialize();
|
|
78368
|
+
const tools = await client.listTools();
|
|
78369
|
+
consola.info(`[external-mcp] "${config.name}" at ${config.url}: ${tools.length} tool(s)`);
|
|
78370
|
+
return {
|
|
78371
|
+
config,
|
|
78372
|
+
tools,
|
|
78373
|
+
instructions: info.instructions
|
|
78374
|
+
};
|
|
78375
|
+
} finally {
|
|
78376
|
+
await client.close();
|
|
78377
|
+
}
|
|
78378
|
+
}
|
|
78379
|
+
/**
|
|
78380
|
+
* Resolve every configured external MCP server for a session, using the cached
|
|
78381
|
+
* tool list where one exists. Servers whose handshake fails are reported in
|
|
78382
|
+
* `failures` and get no namespace — mirroring how a discovered service with a
|
|
78383
|
+
* failed spec download is skipped, except that these were explicitly requested
|
|
78384
|
+
* so the agent is told about them.
|
|
78385
|
+
* @param sessionKey MCP session id, or {@link CLI_EXTERNAL_MCP_SESSION} in CLI mode.
|
|
78386
|
+
* @param configs Parsed connection config.
|
|
78387
|
+
*/
|
|
78388
|
+
async function resolveExternalMcpServers(sessionKey, configs) {
|
|
78389
|
+
if (configs.length === 0) return {
|
|
78390
|
+
servers: [],
|
|
78391
|
+
failures: []
|
|
78392
|
+
};
|
|
78393
|
+
let session = sessions.get(sessionKey);
|
|
78394
|
+
if (!session) {
|
|
78395
|
+
session = { servers: /* @__PURE__ */ new Map() };
|
|
78396
|
+
sessions.set(sessionKey, session);
|
|
78397
|
+
}
|
|
78398
|
+
armIdleTimer(sessionKey);
|
|
78399
|
+
const settled = await Promise.all(configs.map(async (config) => {
|
|
78400
|
+
const key = configKey(config);
|
|
78401
|
+
let pending = session.servers.get(key);
|
|
78402
|
+
if (!pending) {
|
|
78403
|
+
pending = listExternalTools(config);
|
|
78404
|
+
session.servers.set(key, pending);
|
|
78405
|
+
pending.catch(() => {
|
|
78406
|
+
if (session.servers.get(key) === pending) session.servers.delete(key);
|
|
78407
|
+
});
|
|
78408
|
+
}
|
|
78409
|
+
try {
|
|
78410
|
+
return {
|
|
78411
|
+
ok: true,
|
|
78412
|
+
server: await pending
|
|
78413
|
+
};
|
|
78414
|
+
} catch (err) {
|
|
78415
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
78416
|
+
consola.warn(`[external-mcp] "${config.name}" at ${config.url} unavailable:`, reason);
|
|
78417
|
+
return {
|
|
78418
|
+
ok: false,
|
|
78419
|
+
failure: {
|
|
78420
|
+
name: config.name,
|
|
78421
|
+
url: config.url,
|
|
78422
|
+
reason
|
|
78423
|
+
}
|
|
78424
|
+
};
|
|
78425
|
+
}
|
|
78426
|
+
}));
|
|
78427
|
+
return {
|
|
78428
|
+
servers: settled.flatMap((r) => r.ok ? [r.server] : []),
|
|
78429
|
+
failures: settled.flatMap((r) => r.ok ? [] : [r.failure])
|
|
78430
|
+
};
|
|
78431
|
+
}
|
|
78432
|
+
/**
|
|
78433
|
+
* Drop a session's cached external tool lists and its timer.
|
|
78434
|
+
* @param sessionKey MCP session id (or the CLI key).
|
|
78435
|
+
* @param reason Why the entry is being dropped; included in the log line.
|
|
78436
|
+
*/
|
|
78437
|
+
function evictExternalMcpSession(sessionKey, reason) {
|
|
78438
|
+
const session = sessions.get(sessionKey);
|
|
78439
|
+
if (!session) return;
|
|
78440
|
+
if (session.timer) clearTimeout(session.timer);
|
|
78441
|
+
sessions.delete(sessionKey);
|
|
78442
|
+
consola.info(`[external-mcp] dropped cached tool lists for session ${sessionKey} (reason: ${reason})`);
|
|
78443
|
+
}
|
|
78444
|
+
/**
|
|
78445
|
+
* Evict every session (process exit, test cleanup).
|
|
78446
|
+
*/
|
|
78447
|
+
function disposeAllExternalMcpSessions() {
|
|
78448
|
+
for (const sessionKey of [...sessions.keys()]) evictExternalMcpSession(sessionKey, "shutdown");
|
|
78449
|
+
}
|
|
78450
|
+
//#endregion
|
|
77906
78451
|
//#region node_modules/.pnpm/tslib@2.8.1/node_modules/tslib/tslib.es6.mjs
|
|
77907
78452
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
77908
78453
|
function adopt(value) {
|
|
@@ -134718,177 +135263,6 @@ function createC8yAuthHeaders(auth) {
|
|
|
134718
135263
|
throw new Error("Invalid authentication credentials");
|
|
134719
135264
|
}
|
|
134720
135265
|
//#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
135266
|
//#region src/codemode/execute.ts
|
|
134893
135267
|
const EXECUTE_ENTRY_PATH = "/codemode-execute.mjs";
|
|
134894
135268
|
const BLOCKED_REQUEST_PREFIX = "Request blocked by MCP connection policy.";
|
|
@@ -134905,6 +135279,7 @@ async function getSandbox() {
|
|
|
134905
135279
|
}
|
|
134906
135280
|
process$1.once("exit", () => {
|
|
134907
135281
|
disposeAllSandboxSessions();
|
|
135282
|
+
disposeAllExternalMcpSessions();
|
|
134908
135283
|
if (sandboxPromise) sandboxPromise.then((s) => s.dispose()).catch(() => void 0);
|
|
134909
135284
|
});
|
|
134910
135285
|
function formatRestrictionBlockMessage(method, pathname, matching) {
|
|
@@ -135030,29 +135405,39 @@ function buildAgentModule(functionCode) {
|
|
|
135030
135405
|
"export default __mc8ypExecute"
|
|
135031
135406
|
].join("\n");
|
|
135032
135407
|
}
|
|
135033
|
-
function createLiveCalls(
|
|
135408
|
+
function createLiveCalls(tenant) {
|
|
135034
135409
|
const mcpClients = /* @__PURE__ */ new Map();
|
|
135035
|
-
const base = tenantUrl.endsWith("/") ? tenantUrl : `${tenantUrl}/`;
|
|
135036
135410
|
const mcpClientFor = (namespace) => {
|
|
135037
135411
|
let client = mcpClients.get(namespace.name);
|
|
135038
135412
|
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
|
-
})
|
|
135413
|
+
if (namespace.external) client = new McpHttpClient({
|
|
135414
|
+
url: namespace.external.url,
|
|
135415
|
+
fetch: createExternalMcpFetch(namespace.external)
|
|
135048
135416
|
});
|
|
135417
|
+
else {
|
|
135418
|
+
if ("error" in tenant) throw new Error(tenant.error);
|
|
135419
|
+
const { tenantUrl, authHeaders } = tenant;
|
|
135420
|
+
const base = tenantUrl.endsWith("/") ? tenantUrl : `${tenantUrl}/`;
|
|
135421
|
+
client = new McpHttpClient({
|
|
135422
|
+
url: namespace.server.url,
|
|
135423
|
+
fetch: (path, init) => fetch(new URL(path.replace(/^\//, ""), base), {
|
|
135424
|
+
...init,
|
|
135425
|
+
headers: {
|
|
135426
|
+
...init.headers,
|
|
135427
|
+
...namespace.server.sendAuthentication ? authHeaders : {}
|
|
135428
|
+
}
|
|
135429
|
+
})
|
|
135430
|
+
});
|
|
135431
|
+
}
|
|
135049
135432
|
mcpClients.set(namespace.name, client);
|
|
135050
135433
|
}
|
|
135051
135434
|
return client;
|
|
135052
135435
|
};
|
|
135053
135436
|
return {
|
|
135054
135437
|
operation: async (namespace, opName, input) => {
|
|
135055
|
-
|
|
135438
|
+
if ("error" in tenant) throw new Error(tenant.error);
|
|
135439
|
+
const op = namespace.operations.find((o) => o.name === opName);
|
|
135440
|
+
return performRequest(tenant.safeFetch, tenant.tenantUrl, toRequest(op, input));
|
|
135056
135441
|
},
|
|
135057
135442
|
mcpCall: async (namespace, toolName, args) => {
|
|
135058
135443
|
return mcpClientFor(namespace).callTool(toolName, args);
|
|
@@ -135063,17 +135448,7 @@ function createLiveCalls(safeFetch, tenantUrl, authHeaders) {
|
|
|
135063
135448
|
}
|
|
135064
135449
|
};
|
|
135065
135450
|
}
|
|
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) {
|
|
135451
|
+
function buildApiModule(namespaces, methodIndex, docsIndex, live, sandbox, externalFailures) {
|
|
135077
135452
|
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
135453
|
const sandboxEnabled = sandbox !== void 0;
|
|
135079
135454
|
return {
|
|
@@ -135090,9 +135465,15 @@ function buildApiModule(namespaces, methodIndex, docsIndex, live, sandbox) {
|
|
|
135090
135465
|
const targets = target.filter((t) => typeof t === "string" && t.trim() !== "");
|
|
135091
135466
|
if (targets.length === 0) throw new TypeError("codemode.describe(targets): pass method targets like \"c8y.getAlarmCollectionResource\"");
|
|
135092
135467
|
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,
|
|
135468
|
+
return targets.map((t) => describeTarget(namespaces, methodIndex, t, {
|
|
135469
|
+
sandboxEnabled,
|
|
135470
|
+
externalFailures
|
|
135471
|
+
}));
|
|
135094
135472
|
}
|
|
135095
|
-
return describeTarget(namespaces, methodIndex, target == null ? void 0 : String(target),
|
|
135473
|
+
return describeTarget(namespaces, methodIndex, target == null ? void 0 : String(target), {
|
|
135474
|
+
sandboxEnabled,
|
|
135475
|
+
externalFailures
|
|
135476
|
+
});
|
|
135096
135477
|
}
|
|
135097
135478
|
},
|
|
135098
135479
|
docs: {
|
|
@@ -135110,18 +135491,28 @@ function buildApiModule(namespaces, methodIndex, docsIndex, live, sandbox) {
|
|
|
135110
135491
|
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
135492
|
};
|
|
135112
135493
|
}
|
|
135113
|
-
function resolveRuntime() {
|
|
135494
|
+
async function resolveRuntime() {
|
|
135114
135495
|
const custom = c8yMcpServer.ctx.custom;
|
|
135115
135496
|
const resolved = custom?.specs;
|
|
135116
135497
|
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
135498
|
const restrictions = custom?.restrictions ?? [];
|
|
135118
135499
|
const allowRules = custom?.allowRules ?? [];
|
|
135119
135500
|
const noMcp = custom?.noMcp;
|
|
135501
|
+
const { servers: externalServers, failures: externalFailures } = await resolveExternalMcpServers(c8yMcpServer.ctx.sessionId ?? "cli", custom?.externalMcpServers ?? []);
|
|
135502
|
+
const namespaces = buildNamespaces(resolved, {
|
|
135503
|
+
restrictions,
|
|
135504
|
+
allowRules,
|
|
135505
|
+
noMcp,
|
|
135506
|
+
externalServers
|
|
135507
|
+
});
|
|
135508
|
+
const externalNames = new Set(namespaces.filter((ns) => ns.kind === "mcp" && ns.external).map((ns) => ns.name));
|
|
135120
135509
|
return {
|
|
135121
135510
|
resolved,
|
|
135122
135511
|
restrictions,
|
|
135123
135512
|
allowRules,
|
|
135124
|
-
namespaces
|
|
135513
|
+
namespaces,
|
|
135514
|
+
externalFailures,
|
|
135515
|
+
externalMethods: toSearchableMethods(namespaces.filter((ns) => externalNames.has(ns.name)))
|
|
135125
135516
|
};
|
|
135126
135517
|
}
|
|
135127
135518
|
const NO_DEFAULT_EXPORT_MESSAGE = "Execution completed without returning a value.";
|
|
@@ -135137,29 +135528,34 @@ function withCliTenantMarker(text, tenantUrl) {
|
|
|
135137
135528
|
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
135529
|
}
|
|
135139
135530
|
async function execute(functionCode) {
|
|
135140
|
-
const { resolved, namespaces, restrictions, allowRules } = resolveRuntime();
|
|
135531
|
+
const { resolved, namespaces, restrictions, allowRules, externalFailures, externalMethods } = await resolveRuntime();
|
|
135141
135532
|
const SPEC_VIEW = {
|
|
135142
135533
|
all: true,
|
|
135143
135534
|
contextPaths: /* @__PURE__ */ new Set()
|
|
135144
135535
|
};
|
|
135145
|
-
const docsIndex = getDocsIndex(resolved, () => buildNamespaces(resolved,
|
|
135536
|
+
const docsIndex = getDocsIndex(resolved, () => buildNamespaces(resolved, { noMcp: SPEC_VIEW }).filter((ns) => ns.kind === "openapi").map((ns) => ({
|
|
135146
135537
|
namespace: ns.name,
|
|
135147
135538
|
spec: ns.spec
|
|
135148
135539
|
})));
|
|
135149
|
-
const
|
|
135540
|
+
const tenantMethodIndex = getMethodIndex(resolved, () => {
|
|
135150
135541
|
const byTarget = /* @__PURE__ */ new Map();
|
|
135151
|
-
for (const item of [...toSearchableMethods(buildNamespaces(resolved)), ...toSearchableMethods(buildNamespaces(resolved,
|
|
135542
|
+
for (const item of [...toSearchableMethods(buildNamespaces(resolved)), ...toSearchableMethods(buildNamespaces(resolved, { noMcp: SPEC_VIEW }))]) byTarget.set(item.target, item);
|
|
135152
135543
|
return [...byTarget.values()];
|
|
135153
135544
|
});
|
|
135545
|
+
const methodIndex = externalMethods.length === 0 ? tenantMethodIndex : buildMethodIndex([...tenantMethodIndex.methods.values(), ...externalMethods]);
|
|
135154
135546
|
let tenantUrl = null;
|
|
135155
135547
|
let live;
|
|
135156
135548
|
try {
|
|
135157
135549
|
const auth = await resolveC8yAuth();
|
|
135158
135550
|
tenantUrl = auth.tenantUrl;
|
|
135159
135551
|
const authHeaders = createC8yAuthHeaders(auth);
|
|
135160
|
-
live = createLiveCalls(
|
|
135552
|
+
live = createLiveCalls({
|
|
135553
|
+
safeFetch: createCumulocitySafeFetch(auth.tenantUrl, authHeaders, restrictions, allowRules),
|
|
135554
|
+
tenantUrl: auth.tenantUrl,
|
|
135555
|
+
authHeaders
|
|
135556
|
+
});
|
|
135161
135557
|
} catch (error) {
|
|
135162
|
-
live =
|
|
135558
|
+
live = createLiveCalls({ error: error instanceof Error ? error.message : String(error) });
|
|
135163
135559
|
}
|
|
135164
135560
|
const sessionId = c8yMcpServer.ctx.sessionId;
|
|
135165
135561
|
const sandboxApi = c8yMcpServer.ctx.custom?.env === "server" && sessionId && c8yMcpServer.ctx.custom?.enableSandbox ? buildSandboxApi(sessionId) : void 0;
|
|
@@ -135168,7 +135564,7 @@ async function execute(functionCode) {
|
|
|
135168
135564
|
filename: EXECUTE_ENTRY_PATH,
|
|
135169
135565
|
limits: SANDBOX_LIMITS,
|
|
135170
135566
|
imports: {
|
|
135171
|
-
[API_MODULE_SPECIFIER]: buildApiModule(namespaces, methodIndex, docsIndex, live, sandboxApi),
|
|
135567
|
+
[API_MODULE_SPECIFIER]: buildApiModule(namespaces, methodIndex, docsIndex, live, sandboxApi, externalFailures),
|
|
135172
135568
|
[AGENT_MODULE_SPECIFIER]: buildAgentModule(functionCode)
|
|
135173
135569
|
}
|
|
135174
135570
|
}).finally(() => live.dispose());
|
|
@@ -135255,10 +135651,11 @@ declare const docs: {
|
|
|
135255
135651
|
}
|
|
135256
135652
|
|
|
135257
135653
|
// API namespaces: \`c8y\` (Cumulocity core — always present) plus one global
|
|
135258
|
-
// per microservice available on the current tenant (e.g. \`dtm\`),
|
|
135259
|
-
//
|
|
135260
|
-
//
|
|
135261
|
-
//
|
|
135654
|
+
// per microservice available on the current tenant (e.g. \`dtm\`), plus any
|
|
135655
|
+
// external MCP server configured for this connection — each with one typed
|
|
135656
|
+
// method per operation. \`codemode.describe()\` lists what this connection
|
|
135657
|
+
// actually has. If a method seems missing, search with different wording; if
|
|
135658
|
+
// it truly does not exist, say so instead of improvising:
|
|
135262
135659
|
// await c8y.getManagedObjectCollectionResource({ pageSize: 5 })
|
|
135263
135660
|
\`\`\`
|
|
135264
135661
|
|
|
@@ -140737,17 +141134,17 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140737
141134
|
"openapi": "3.0.1",
|
|
140738
141135
|
"info": {
|
|
140739
141136
|
"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
|
|
141137
|
+
"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
141138
|
"version": "Latest"
|
|
140742
141139
|
},
|
|
140743
141140
|
"servers": [{
|
|
140744
|
-
"url": "
|
|
141141
|
+
"url": "/",
|
|
140745
141142
|
"description": "The Digital Twin Manager service."
|
|
140746
141143
|
}],
|
|
140747
141144
|
"tags": [
|
|
140748
141145
|
{
|
|
140749
141146
|
"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\
|
|
141147
|
+
"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
141148
|
},
|
|
140752
141149
|
{
|
|
140753
141150
|
"name": "Linked Series",
|
|
@@ -140779,7 +141176,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140779
141176
|
"get": {
|
|
140780
141177
|
"tags": ["Property Definitions"],
|
|
140781
141178
|
"summary": "Retrieve a Property Definition by identifier",
|
|
140782
|
-
"description": "Finds a `Property Definition` by its `identifier`.\n\n
|
|
141179
|
+
"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
141180
|
"operationId": "getPropertyDefinition",
|
|
140784
141181
|
"parameters": [{
|
|
140785
141182
|
"name": "identifier",
|
|
@@ -140790,7 +141187,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140790
141187
|
}, {
|
|
140791
141188
|
"name": "applicableTo",
|
|
140792
141189
|
"in": "query",
|
|
140793
|
-
"description": "Limits the response to Property Definitions applicable to the specified domain entity.",
|
|
141190
|
+
"description": "Limits the response to the Property Definitions that are applicable to the specified domain entity.",
|
|
140794
141191
|
"schema": {
|
|
140795
141192
|
"type": "string",
|
|
140796
141193
|
"enum": [
|
|
@@ -140812,7 +141209,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140812
141209
|
"put": {
|
|
140813
141210
|
"tags": ["Property Definitions"],
|
|
140814
141211
|
"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.",
|
|
141212
|
+
"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
141213
|
"operationId": "updatePropertyDefinition",
|
|
140817
141214
|
"parameters": [{
|
|
140818
141215
|
"name": "identifier",
|
|
@@ -140823,7 +141220,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140823
141220
|
}, {
|
|
140824
141221
|
"name": "contexts",
|
|
140825
141222
|
"in": "query",
|
|
140826
|
-
"description": "
|
|
141223
|
+
"description": "Indicates the resource to filter for the Property Definition that matches exactly the specified domain entities.",
|
|
140827
141224
|
"explode": false,
|
|
140828
141225
|
"schema": {
|
|
140829
141226
|
"type": "array",
|
|
@@ -140854,7 +141251,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140854
141251
|
"delete": {
|
|
140855
141252
|
"tags": ["Property Definitions"],
|
|
140856
141253
|
"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.",
|
|
141254
|
+
"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
141255
|
"operationId": "deletePropertyDefinition",
|
|
140859
141256
|
"parameters": [{
|
|
140860
141257
|
"name": "identifier",
|
|
@@ -140865,7 +141262,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140865
141262
|
}, {
|
|
140866
141263
|
"name": "contexts",
|
|
140867
141264
|
"in": "query",
|
|
140868
|
-
"description": "
|
|
141265
|
+
"description": "Indicates the resource to filter for the Property Definition that matches exactly the specified domain entities.",
|
|
140869
141266
|
"explode": false,
|
|
140870
141267
|
"schema": {
|
|
140871
141268
|
"type": "array",
|
|
@@ -140890,7 +141287,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140890
141287
|
"get": {
|
|
140891
141288
|
"tags": ["Measurement Definitions"],
|
|
140892
141289
|
"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.*",
|
|
141290
|
+
"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
141291
|
"operationId": "getMeasurementDefinitions",
|
|
140895
141292
|
"parameters": [
|
|
140896
141293
|
{
|
|
@@ -140951,9 +141348,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140951
141348
|
"in": "query",
|
|
140952
141349
|
"description": "The current page number to be retrieved.",
|
|
140953
141350
|
"schema": {
|
|
140954
|
-
"
|
|
140955
|
-
"
|
|
140956
|
-
"default": 1
|
|
141351
|
+
"minimum": 1,
|
|
141352
|
+
"type": "integer"
|
|
140957
141353
|
}
|
|
140958
141354
|
},
|
|
140959
141355
|
{
|
|
@@ -140975,7 +141371,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140975
141371
|
"put": {
|
|
140976
141372
|
"tags": ["Measurement Definitions"],
|
|
140977
141373
|
"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.",
|
|
141374
|
+
"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
141375
|
"operationId": "updateMeasurementDefinition",
|
|
140980
141376
|
"requestBody": {
|
|
140981
141377
|
"description": "The data payload representing the `Measurement Definition` to be updated.",
|
|
@@ -140990,7 +141386,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
140990
141386
|
"post": {
|
|
140991
141387
|
"tags": ["Measurement Definitions"],
|
|
140992
141388
|
"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).",
|
|
141389
|
+
"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
141390
|
"operationId": "createMeasurementDefinition",
|
|
140995
141391
|
"requestBody": {
|
|
140996
141392
|
"description": "The data payload representing the `Measurement Definition` to be created.",
|
|
@@ -141007,7 +141403,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141007
141403
|
"get": {
|
|
141008
141404
|
"tags": ["Event Definitions"],
|
|
141009
141405
|
"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.*",
|
|
141406
|
+
"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
141407
|
"operationId": "getEventDefinitions",
|
|
141012
141408
|
"parameters": [
|
|
141013
141409
|
{
|
|
@@ -141068,9 +141464,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141068
141464
|
"in": "query",
|
|
141069
141465
|
"description": "The current page number to be retrieved.",
|
|
141070
141466
|
"schema": {
|
|
141071
|
-
"
|
|
141072
|
-
"
|
|
141073
|
-
"default": 1
|
|
141467
|
+
"minimum": 1,
|
|
141468
|
+
"type": "integer"
|
|
141074
141469
|
}
|
|
141075
141470
|
},
|
|
141076
141471
|
{
|
|
@@ -141092,7 +141487,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141092
141487
|
"put": {
|
|
141093
141488
|
"tags": ["Event Definitions"],
|
|
141094
141489
|
"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.",
|
|
141490
|
+
"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
141491
|
"operationId": "updateEventDefinition",
|
|
141097
141492
|
"requestBody": {
|
|
141098
141493
|
"description": "The data payload representing the `Event Definition` to be updated.",
|
|
@@ -141107,7 +141502,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141107
141502
|
"post": {
|
|
141108
141503
|
"tags": ["Event Definitions"],
|
|
141109
141504
|
"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).",
|
|
141505
|
+
"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
141506
|
"operationId": "createEventDefinition",
|
|
141112
141507
|
"requestBody": {
|
|
141113
141508
|
"description": "The data payload representing the `Event Definition` to be created.",
|
|
@@ -141124,7 +141519,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141124
141519
|
"get": {
|
|
141125
141520
|
"tags": ["Asset Definitions"],
|
|
141126
141521
|
"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.*",
|
|
141522
|
+
"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
141523
|
"operationId": "getAssetDefinitions",
|
|
141129
141524
|
"parameters": [
|
|
141130
141525
|
{
|
|
@@ -141194,9 +141589,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141194
141589
|
"in": "query",
|
|
141195
141590
|
"description": "The current page number to be retrieved.",
|
|
141196
141591
|
"schema": {
|
|
141197
|
-
"
|
|
141198
|
-
"
|
|
141199
|
-
"default": 1
|
|
141592
|
+
"minimum": 1,
|
|
141593
|
+
"type": "integer"
|
|
141200
141594
|
}
|
|
141201
141595
|
},
|
|
141202
141596
|
{
|
|
@@ -141218,7 +141612,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141218
141612
|
"put": {
|
|
141219
141613
|
"tags": ["Asset Definitions"],
|
|
141220
141614
|
"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`*",
|
|
141615
|
+
"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
141616
|
"operationId": "updateAssetDefinition",
|
|
141223
141617
|
"requestBody": {
|
|
141224
141618
|
"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 +141644,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141250
141644
|
"get": {
|
|
141251
141645
|
"tags": ["Alarm Definitions"],
|
|
141252
141646
|
"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.*",
|
|
141647
|
+
"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
141648
|
"operationId": "getAlarmDefinitions",
|
|
141255
141649
|
"parameters": [
|
|
141256
141650
|
{
|
|
@@ -141311,9 +141705,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141311
141705
|
"in": "query",
|
|
141312
141706
|
"description": "The current page number to be retrieved.",
|
|
141313
141707
|
"schema": {
|
|
141314
|
-
"
|
|
141315
|
-
"
|
|
141316
|
-
"default": 1
|
|
141708
|
+
"minimum": 1,
|
|
141709
|
+
"type": "integer"
|
|
141317
141710
|
}
|
|
141318
141711
|
},
|
|
141319
141712
|
{
|
|
@@ -141335,7 +141728,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141335
141728
|
"put": {
|
|
141336
141729
|
"tags": ["Alarm Definitions"],
|
|
141337
141730
|
"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.",
|
|
141731
|
+
"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
141732
|
"operationId": "updateAlarmDefinition",
|
|
141340
141733
|
"requestBody": {
|
|
141341
141734
|
"description": "The data payload representing the `Alarm Definition` to be updated.",
|
|
@@ -141350,7 +141743,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141350
141743
|
"post": {
|
|
141351
141744
|
"tags": ["Alarm Definitions"],
|
|
141352
141745
|
"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).",
|
|
141746
|
+
"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
141747
|
"operationId": "createAlarmDefinition",
|
|
141355
141748
|
"requestBody": {
|
|
141356
141749
|
"description": "The data payload representing the `Alarm Definition` to be created.",
|
|
@@ -141388,7 +141781,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141388
141781
|
{
|
|
141389
141782
|
"name": "withSubAssets",
|
|
141390
141783
|
"in": "query",
|
|
141391
|
-
"description": "Indicates the resource to add the sub-assets in the response.",
|
|
141784
|
+
"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.",
|
|
141785
|
+
"deprecated": true,
|
|
141392
141786
|
"schema": {
|
|
141393
141787
|
"type": "boolean",
|
|
141394
141788
|
"default": false
|
|
@@ -141402,6 +141796,24 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141402
141796
|
"type": "boolean",
|
|
141403
141797
|
"default": false
|
|
141404
141798
|
}
|
|
141799
|
+
},
|
|
141800
|
+
{
|
|
141801
|
+
"name": "withParents",
|
|
141802
|
+
"in": "query",
|
|
141803
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
141804
|
+
"schema": {
|
|
141805
|
+
"type": "boolean",
|
|
141806
|
+
"default": false
|
|
141807
|
+
}
|
|
141808
|
+
},
|
|
141809
|
+
{
|
|
141810
|
+
"name": "withChildrenCount",
|
|
141811
|
+
"in": "query",
|
|
141812
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
141813
|
+
"schema": {
|
|
141814
|
+
"type": "boolean",
|
|
141815
|
+
"default": false
|
|
141816
|
+
}
|
|
141405
141817
|
}
|
|
141406
141818
|
],
|
|
141407
141819
|
"responses": { "200": {
|
|
@@ -141412,7 +141824,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141412
141824
|
"put": {
|
|
141413
141825
|
"tags": ["Assets"],
|
|
141414
141826
|
"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`*",
|
|
141827
|
+
"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
141828
|
"operationId": "updateAsset",
|
|
141417
141829
|
"parameters": [{
|
|
141418
141830
|
"name": "assetId",
|
|
@@ -141440,14 +141852,34 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141440
141852
|
"delete": {
|
|
141441
141853
|
"tags": ["Assets"],
|
|
141442
141854
|
"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`*",
|
|
141855
|
+
"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
141856
|
"operationId": "deleteAsset",
|
|
141445
|
-
"parameters": [
|
|
141446
|
-
|
|
141447
|
-
|
|
141448
|
-
|
|
141449
|
-
|
|
141450
|
-
|
|
141857
|
+
"parameters": [
|
|
141858
|
+
{
|
|
141859
|
+
"name": "assetId",
|
|
141860
|
+
"in": "path",
|
|
141861
|
+
"required": true,
|
|
141862
|
+
"schema": { "type": "string" }
|
|
141863
|
+
},
|
|
141864
|
+
{
|
|
141865
|
+
"name": "deleteSubAssets",
|
|
141866
|
+
"in": "query",
|
|
141867
|
+
"description": "Indicates whether the resource also deletes sub-assets. If `false`, sub-assets are only unassigned from the deleted asset but not deleted themselves.",
|
|
141868
|
+
"schema": {
|
|
141869
|
+
"type": "boolean",
|
|
141870
|
+
"default": true
|
|
141871
|
+
}
|
|
141872
|
+
},
|
|
141873
|
+
{
|
|
141874
|
+
"name": "deleteDevices",
|
|
141875
|
+
"in": "query",
|
|
141876
|
+
"description": "Indicates whether the resource also deletes devices (managed objects with the `c8y_IsDevice` fragment) whose all parent assets are being deleted.",
|
|
141877
|
+
"schema": {
|
|
141878
|
+
"type": "boolean",
|
|
141879
|
+
"default": false
|
|
141880
|
+
}
|
|
141881
|
+
}
|
|
141882
|
+
],
|
|
141451
141883
|
"responses": { "204": { "description": "No Content" } }
|
|
141452
141884
|
}
|
|
141453
141885
|
},
|
|
@@ -141455,7 +141887,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141455
141887
|
"get": {
|
|
141456
141888
|
"tags": ["Linked Series"],
|
|
141457
141889
|
"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`.",
|
|
141890
|
+
"description": "Retrieves the source for a given linked series of an asset.\n\n Linked series are identified by their `fragment` **and** `series`. ",
|
|
141459
141891
|
"operationId": "getLinkedSeriesSource",
|
|
141460
141892
|
"parameters": [
|
|
141461
141893
|
{
|
|
@@ -141485,7 +141917,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141485
141917
|
"put": {
|
|
141486
141918
|
"tags": ["Linked Series"],
|
|
141487
141919
|
"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`*.",
|
|
141920
|
+
"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
141921
|
"operationId": "updateLinkedSeriesSource",
|
|
141490
141922
|
"parameters": [
|
|
141491
141923
|
{
|
|
@@ -141528,7 +141960,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141528
141960
|
"post": {
|
|
141529
141961
|
"tags": ["Linked Series"],
|
|
141530
141962
|
"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`*.",
|
|
141963
|
+
"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
141964
|
"operationId": "createLinkedSeriesSource",
|
|
141533
141965
|
"parameters": [
|
|
141534
141966
|
{
|
|
@@ -141571,7 +142003,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141571
142003
|
"delete": {
|
|
141572
142004
|
"tags": ["Linked Series"],
|
|
141573
142005
|
"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`*.",
|
|
142006
|
+
"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
142007
|
"operationId": "deleteLinkedSeriesSource",
|
|
141576
142008
|
"parameters": [
|
|
141577
142009
|
{
|
|
@@ -141599,7 +142031,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141599
142031
|
"/service/dtm/assets/{assetId}/linkedSeries/{fragment}/{series}/source/type": { "put": {
|
|
141600
142032
|
"tags": ["Linked Series"],
|
|
141601
142033
|
"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
|
|
142034
|
+
"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
142035
|
"operationId": "updateMeasurementType",
|
|
141604
142036
|
"parameters": [
|
|
141605
142037
|
{
|
|
@@ -141630,16 +142062,61 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141630
142062
|
}
|
|
141631
142063
|
}
|
|
141632
142064
|
],
|
|
141633
|
-
"requestBody": { "content": {
|
|
142065
|
+
"requestBody": { "content": {
|
|
142066
|
+
"text/plain": { "schema": { "type": "string" } },
|
|
142067
|
+
"application/json": { "schema": { "type": "string" } }
|
|
142068
|
+
} },
|
|
141634
142069
|
"responses": { "200": {
|
|
141635
142070
|
"description": "OK",
|
|
141636
142071
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Source" } } }
|
|
141637
142072
|
} }
|
|
141638
142073
|
} },
|
|
142074
|
+
"/service/dtm/assets/{assetId}/linkedSeries/{fragment}/{series}/reconcileOpposite": { "put": {
|
|
142075
|
+
"tags": ["Linked Series"],
|
|
142076
|
+
"summary": "Reconcile the opposite link for a linked series",
|
|
142077
|
+
"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`*. ",
|
|
142078
|
+
"operationId": "reconcileOppositeLink",
|
|
142079
|
+
"parameters": [
|
|
142080
|
+
{
|
|
142081
|
+
"name": "assetId",
|
|
142082
|
+
"in": "path",
|
|
142083
|
+
"required": true,
|
|
142084
|
+
"schema": { "type": "string" }
|
|
142085
|
+
},
|
|
142086
|
+
{
|
|
142087
|
+
"name": "fragment",
|
|
142088
|
+
"in": "path",
|
|
142089
|
+
"required": true,
|
|
142090
|
+
"schema": { "type": "string" }
|
|
142091
|
+
},
|
|
142092
|
+
{
|
|
142093
|
+
"name": "series",
|
|
142094
|
+
"in": "path",
|
|
142095
|
+
"required": true,
|
|
142096
|
+
"schema": { "type": "string" }
|
|
142097
|
+
},
|
|
142098
|
+
{
|
|
142099
|
+
"name": "removeMissingSourceId",
|
|
142100
|
+
"in": "query",
|
|
142101
|
+
"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.",
|
|
142102
|
+
"schema": {
|
|
142103
|
+
"type": "boolean",
|
|
142104
|
+
"default": false
|
|
142105
|
+
}
|
|
142106
|
+
}
|
|
142107
|
+
],
|
|
142108
|
+
"responses": {
|
|
142109
|
+
"200": {
|
|
142110
|
+
"description": "OK",
|
|
142111
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkedAsset" } } }
|
|
142112
|
+
},
|
|
142113
|
+
"204": { "description": "No Content" }
|
|
142114
|
+
}
|
|
142115
|
+
} },
|
|
141639
142116
|
"/service/dtm/assets/{assetId}/linkedSeries/{fragment}/{series}/label": { "put": {
|
|
141640
142117
|
"tags": ["Linked Series"],
|
|
141641
142118
|
"summary": "Update the label of a linked series",
|
|
141642
|
-
"description": "Modifies the `label` of a linked series for a given asset.",
|
|
142119
|
+
"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
142120
|
"operationId": "updateLabel",
|
|
141644
142121
|
"parameters": [
|
|
141645
142122
|
{
|
|
@@ -141662,7 +142139,10 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141662
142139
|
}
|
|
141663
142140
|
],
|
|
141664
142141
|
"requestBody": {
|
|
141665
|
-
"content": {
|
|
142142
|
+
"content": {
|
|
142143
|
+
"text/plain": { "schema": { "type": "string" } },
|
|
142144
|
+
"application/json": { "schema": { "type": "string" } }
|
|
142145
|
+
},
|
|
141666
142146
|
"required": true
|
|
141667
142147
|
},
|
|
141668
142148
|
"responses": { "200": {
|
|
@@ -141670,11 +142150,41 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141670
142150
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkedSeries" } } }
|
|
141671
142151
|
} }
|
|
141672
142152
|
} },
|
|
142153
|
+
"/service/dtm/assets/{assetId}/linkedSeries/reconcileOpposite": { "put": {
|
|
142154
|
+
"tags": ["Linked Series"],
|
|
142155
|
+
"summary": "Reconcile the opposite links for all linked series of an asset",
|
|
142156
|
+
"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`*. ",
|
|
142157
|
+
"operationId": "reconcileOppositeLinks",
|
|
142158
|
+
"parameters": [{
|
|
142159
|
+
"name": "assetId",
|
|
142160
|
+
"in": "path",
|
|
142161
|
+
"required": true,
|
|
142162
|
+
"schema": { "type": "string" }
|
|
142163
|
+
}, {
|
|
142164
|
+
"name": "removeMissingSourceId",
|
|
142165
|
+
"in": "query",
|
|
142166
|
+
"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.",
|
|
142167
|
+
"schema": {
|
|
142168
|
+
"type": "boolean",
|
|
142169
|
+
"default": false
|
|
142170
|
+
}
|
|
142171
|
+
}],
|
|
142172
|
+
"responses": {
|
|
142173
|
+
"200": {
|
|
142174
|
+
"description": "OK",
|
|
142175
|
+
"content": { "application/json": { "schema": {
|
|
142176
|
+
"type": "array",
|
|
142177
|
+
"items": { "$ref": "#/components/schemas/LinkedAsset" }
|
|
142178
|
+
} } }
|
|
142179
|
+
},
|
|
142180
|
+
"204": { "description": "No Content" }
|
|
142181
|
+
}
|
|
142182
|
+
} },
|
|
141673
142183
|
"/service/dtm/definitions/properties": {
|
|
141674
142184
|
"get": {
|
|
141675
142185
|
"tags": ["Property Definitions"],
|
|
141676
142186
|
"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>",
|
|
142187
|
+
"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
142188
|
"operationId": "getPropertyDefinitions",
|
|
141679
142189
|
"parameters": [
|
|
141680
142190
|
{
|
|
@@ -141739,7 +142249,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141739
142249
|
{
|
|
141740
142250
|
"name": "applicableTo",
|
|
141741
142251
|
"in": "query",
|
|
141742
|
-
"description": "Limits the response to Property Definitions applicable to the specified domain entity.",
|
|
142252
|
+
"description": "Limits the response to the Property Definitions that are applicable to the specified domain entity.",
|
|
141743
142253
|
"schema": {
|
|
141744
142254
|
"type": "string",
|
|
141745
142255
|
"enum": [
|
|
@@ -141758,9 +142268,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141758
142268
|
"in": "query",
|
|
141759
142269
|
"description": "The current page number to be retrieved.",
|
|
141760
142270
|
"schema": {
|
|
141761
|
-
"
|
|
141762
|
-
"
|
|
141763
|
-
"default": 1
|
|
142271
|
+
"minimum": 1,
|
|
142272
|
+
"type": "integer"
|
|
141764
142273
|
}
|
|
141765
142274
|
},
|
|
141766
142275
|
{
|
|
@@ -141782,7 +142291,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141782
142291
|
"post": {
|
|
141783
142292
|
"tags": ["Property Definitions"],
|
|
141784
142293
|
"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).",
|
|
142294
|
+
"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
142295
|
"operationId": "createPropertyDefinition",
|
|
141787
142296
|
"requestBody": {
|
|
141788
142297
|
"description": "The data payload representing the `Property Definition` to be created.",
|
|
@@ -141798,7 +142307,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141798
142307
|
"/service/dtm/definitions/properties/compose": { "post": {
|
|
141799
142308
|
"tags": ["Property Definitions"],
|
|
141800
142309
|
"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.",
|
|
142310
|
+
"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
142311
|
"operationId": "composePropertyDefinitions",
|
|
141803
142312
|
"requestBody": {
|
|
141804
142313
|
"content": { "application/json": { "schema": {
|
|
@@ -141816,7 +142325,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141816
142325
|
"get": {
|
|
141817
142326
|
"tags": ["Assets"],
|
|
141818
142327
|
"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.",
|
|
142328
|
+
"description": "Retrieves all assets registered in your tenant.\n\n The `query` parameter supports a flexible syntax for filtering and ordering results. ",
|
|
141820
142329
|
"operationId": "getAssets",
|
|
141821
142330
|
"parameters": [
|
|
141822
142331
|
{
|
|
@@ -141854,7 +142363,17 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141854
142363
|
{
|
|
141855
142364
|
"name": "withSubAssets",
|
|
141856
142365
|
"in": "query",
|
|
141857
|
-
"description": "Indicates the resource to add the sub-assets in the response.",
|
|
142366
|
+
"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.",
|
|
142367
|
+
"deprecated": true,
|
|
142368
|
+
"schema": {
|
|
142369
|
+
"type": "boolean",
|
|
142370
|
+
"default": false
|
|
142371
|
+
}
|
|
142372
|
+
},
|
|
142373
|
+
{
|
|
142374
|
+
"name": "withParents",
|
|
142375
|
+
"in": "query",
|
|
142376
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
141858
142377
|
"schema": {
|
|
141859
142378
|
"type": "boolean",
|
|
141860
142379
|
"default": false
|
|
@@ -141868,13 +142387,30 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141868
142387
|
"default": false
|
|
141869
142388
|
}
|
|
141870
142389
|
},
|
|
142390
|
+
{
|
|
142391
|
+
"name": "includeGroups",
|
|
142392
|
+
"in": "query",
|
|
142393
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
142394
|
+
"schema": {
|
|
142395
|
+
"type": "boolean",
|
|
142396
|
+
"default": false
|
|
142397
|
+
}
|
|
142398
|
+
},
|
|
142399
|
+
{
|
|
142400
|
+
"name": "withChildrenCount",
|
|
142401
|
+
"in": "query",
|
|
142402
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
142403
|
+
"schema": {
|
|
142404
|
+
"type": "boolean",
|
|
142405
|
+
"default": false
|
|
142406
|
+
}
|
|
142407
|
+
},
|
|
141871
142408
|
{
|
|
141872
142409
|
"name": "currentPage",
|
|
141873
142410
|
"in": "query",
|
|
141874
142411
|
"schema": {
|
|
141875
|
-
"
|
|
141876
|
-
"
|
|
141877
|
-
"default": 1
|
|
142412
|
+
"minimum": 1,
|
|
142413
|
+
"type": "integer"
|
|
141878
142414
|
}
|
|
141879
142415
|
},
|
|
141880
142416
|
{
|
|
@@ -141895,7 +142431,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141895
142431
|
"post": {
|
|
141896
142432
|
"tags": ["Assets"],
|
|
141897
142433
|
"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.",
|
|
142434
|
+
"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
142435
|
"operationId": "createAsset",
|
|
141900
142436
|
"parameters": [{
|
|
141901
142437
|
"name": "fetchTypeFromSource",
|
|
@@ -141927,11 +142463,127 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141927
142463
|
}
|
|
141928
142464
|
}
|
|
141929
142465
|
},
|
|
142466
|
+
"/service/dtm/assets/{assetId}/subAssets": {
|
|
142467
|
+
"get": {
|
|
142468
|
+
"tags": ["Assets"],
|
|
142469
|
+
"summary": "Retrieve sub-assets of an asset",
|
|
142470
|
+
"description": "Retrieves sub-assets of an asset.\n\n Fetches all sub-assets of the parent asset specified by `assetId`. ",
|
|
142471
|
+
"operationId": "getSubAssets",
|
|
142472
|
+
"parameters": [
|
|
142473
|
+
{
|
|
142474
|
+
"name": "assetId",
|
|
142475
|
+
"in": "path",
|
|
142476
|
+
"required": true,
|
|
142477
|
+
"schema": { "type": "string" }
|
|
142478
|
+
},
|
|
142479
|
+
{
|
|
142480
|
+
"name": "query",
|
|
142481
|
+
"in": "query",
|
|
142482
|
+
"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).",
|
|
142483
|
+
"schema": {
|
|
142484
|
+
"type": "string",
|
|
142485
|
+
"format": "c8y:query"
|
|
142486
|
+
},
|
|
142487
|
+
"examples": {
|
|
142488
|
+
"Filter by name": {
|
|
142489
|
+
"description": "Filter by name",
|
|
142490
|
+
"value": "$filter=name eq 'Windfarm'"
|
|
142491
|
+
},
|
|
142492
|
+
"Filter by type": {
|
|
142493
|
+
"description": "Filter by type",
|
|
142494
|
+
"value": "$filter=type eq 'c8y_*'"
|
|
142495
|
+
},
|
|
142496
|
+
"Using orderby": {
|
|
142497
|
+
"description": "Using orderby",
|
|
142498
|
+
"value": "$orderby=id asc"
|
|
142499
|
+
}
|
|
142500
|
+
}
|
|
142501
|
+
},
|
|
142502
|
+
{
|
|
142503
|
+
"name": "withLinkedSeries",
|
|
142504
|
+
"in": "query",
|
|
142505
|
+
"description": "Indicates the resource to add the `c8y_LinkedSeries` fragment in the response.",
|
|
142506
|
+
"schema": {
|
|
142507
|
+
"type": "boolean",
|
|
142508
|
+
"default": false
|
|
142509
|
+
}
|
|
142510
|
+
},
|
|
142511
|
+
{
|
|
142512
|
+
"name": "withParents",
|
|
142513
|
+
"in": "query",
|
|
142514
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
142515
|
+
"schema": {
|
|
142516
|
+
"type": "boolean",
|
|
142517
|
+
"default": false
|
|
142518
|
+
}
|
|
142519
|
+
},
|
|
142520
|
+
{
|
|
142521
|
+
"name": "includeGroups",
|
|
142522
|
+
"in": "query",
|
|
142523
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
142524
|
+
"schema": {
|
|
142525
|
+
"type": "boolean",
|
|
142526
|
+
"default": false
|
|
142527
|
+
}
|
|
142528
|
+
},
|
|
142529
|
+
{
|
|
142530
|
+
"name": "withChildrenCount",
|
|
142531
|
+
"in": "query",
|
|
142532
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
142533
|
+
"schema": {
|
|
142534
|
+
"type": "boolean",
|
|
142535
|
+
"default": false
|
|
142536
|
+
}
|
|
142537
|
+
},
|
|
142538
|
+
{
|
|
142539
|
+
"name": "currentPage",
|
|
142540
|
+
"in": "query",
|
|
142541
|
+
"schema": {
|
|
142542
|
+
"minimum": 1,
|
|
142543
|
+
"type": "integer"
|
|
142544
|
+
}
|
|
142545
|
+
},
|
|
142546
|
+
{
|
|
142547
|
+
"name": "pageSize",
|
|
142548
|
+
"in": "query",
|
|
142549
|
+
"schema": {
|
|
142550
|
+
"maximum": 2e3,
|
|
142551
|
+
"minimum": 1,
|
|
142552
|
+
"type": "integer"
|
|
142553
|
+
}
|
|
142554
|
+
}
|
|
142555
|
+
],
|
|
142556
|
+
"responses": { "200": {
|
|
142557
|
+
"description": "OK",
|
|
142558
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedAssetList" } } }
|
|
142559
|
+
} }
|
|
142560
|
+
},
|
|
142561
|
+
"post": {
|
|
142562
|
+
"tags": ["Assets"],
|
|
142563
|
+
"summary": "Create and assign a sub-asset in one operation",
|
|
142564
|
+
"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) ",
|
|
142565
|
+
"operationId": "createAndAssignSubAsset",
|
|
142566
|
+
"parameters": [{
|
|
142567
|
+
"name": "assetId",
|
|
142568
|
+
"in": "path",
|
|
142569
|
+
"required": true,
|
|
142570
|
+
"schema": { "type": "string" }
|
|
142571
|
+
}],
|
|
142572
|
+
"requestBody": {
|
|
142573
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Asset" } } },
|
|
142574
|
+
"required": true
|
|
142575
|
+
},
|
|
142576
|
+
"responses": { "201": {
|
|
142577
|
+
"description": "Created",
|
|
142578
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Asset" } } }
|
|
142579
|
+
} }
|
|
142580
|
+
}
|
|
142581
|
+
},
|
|
141930
142582
|
"/service/dtm/assets/{assetId}/subAssets/{subAssetId}": {
|
|
141931
142583
|
"post": {
|
|
141932
142584
|
"tags": ["Assets"],
|
|
141933
142585
|
"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`*",
|
|
142586
|
+
"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
142587
|
"operationId": "assignSubAsset",
|
|
141936
142588
|
"parameters": [{
|
|
141937
142589
|
"name": "assetId",
|
|
@@ -141952,7 +142604,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141952
142604
|
"delete": {
|
|
141953
142605
|
"tags": ["Assets"],
|
|
141954
142606
|
"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`*",
|
|
142607
|
+
"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
142608
|
"operationId": "unassignSubAsset",
|
|
141957
142609
|
"parameters": [{
|
|
141958
142610
|
"name": "assetId",
|
|
@@ -141972,7 +142624,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
141972
142624
|
"get": {
|
|
141973
142625
|
"tags": ["Linked Series"],
|
|
141974
142626
|
"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.",
|
|
142627
|
+
"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
142628
|
"operationId": "getLinkedSeries",
|
|
141977
142629
|
"parameters": [
|
|
141978
142630
|
{
|
|
@@ -142005,7 +142657,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142005
142657
|
{
|
|
142006
142658
|
"name": "withJsonSchema",
|
|
142007
142659
|
"in": "query",
|
|
142008
|
-
"description": "If true
|
|
142660
|
+
"description": "If `true`, the response includes the inferred JSON schema describing all linked series of this Asset.",
|
|
142009
142661
|
"schema": {
|
|
142010
142662
|
"type": "boolean",
|
|
142011
142663
|
"default": false
|
|
@@ -142015,9 +142667,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142015
142667
|
"name": "currentPage",
|
|
142016
142668
|
"in": "query",
|
|
142017
142669
|
"schema": {
|
|
142018
|
-
"
|
|
142019
|
-
"
|
|
142020
|
-
"default": 1
|
|
142670
|
+
"minimum": 1,
|
|
142671
|
+
"type": "integer"
|
|
142021
142672
|
}
|
|
142022
142673
|
},
|
|
142023
142674
|
{
|
|
@@ -142044,7 +142695,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142044
142695
|
"post": {
|
|
142045
142696
|
"tags": ["Linked Series"],
|
|
142046
142697
|
"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`*.",
|
|
142698
|
+
"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
142699
|
"operationId": "addAndUpdateLinkedSeries",
|
|
142049
142700
|
"parameters": [{
|
|
142050
142701
|
"name": "assetId",
|
|
@@ -142075,7 +142726,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142075
142726
|
"delete": {
|
|
142076
142727
|
"tags": ["Linked Series"],
|
|
142077
142728
|
"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`*.",
|
|
142729
|
+
"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
142730
|
"operationId": "deleteLinkedSeries",
|
|
142080
142731
|
"parameters": [
|
|
142081
142732
|
{
|
|
@@ -142104,7 +142755,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142104
142755
|
"post": {
|
|
142105
142756
|
"tags": ["Assets"],
|
|
142106
142757
|
"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
|
|
142758
|
+
"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
142759
|
"operationId": "assignDevice",
|
|
142109
142760
|
"parameters": [{
|
|
142110
142761
|
"name": "assetId",
|
|
@@ -142125,7 +142776,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142125
142776
|
"delete": {
|
|
142126
142777
|
"tags": ["Assets"],
|
|
142127
142778
|
"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
|
|
142779
|
+
"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
142780
|
"operationId": "unassignDevice",
|
|
142130
142781
|
"parameters": [{
|
|
142131
142782
|
"name": "assetId",
|
|
@@ -142176,7 +142827,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142176
142827
|
"/service/dtm/definitions/properties/count": { "get": {
|
|
142177
142828
|
"tags": ["Property Definitions"],
|
|
142178
142829
|
"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.*",
|
|
142830
|
+
"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
142831
|
"operationId": "countPropertyDefinitions",
|
|
142181
142832
|
"parameters": [
|
|
142182
142833
|
{
|
|
@@ -142212,7 +142863,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142212
142863
|
{
|
|
142213
142864
|
"name": "applicableTo",
|
|
142214
142865
|
"in": "query",
|
|
142215
|
-
"description": "Limits the response to Property Definitions applicable to the specified domain entity.",
|
|
142866
|
+
"description": "Limits the response to the Property Definitions that are applicable to the specified domain entity.",
|
|
142216
142867
|
"schema": {
|
|
142217
142868
|
"type": "string",
|
|
142218
142869
|
"enum": [
|
|
@@ -142236,7 +142887,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142236
142887
|
"get": {
|
|
142237
142888
|
"tags": ["Measurement Definitions"],
|
|
142238
142889
|
"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`.",
|
|
142890
|
+
"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
142891
|
"operationId": "getMeasurementDefinition",
|
|
142241
142892
|
"parameters": [{
|
|
142242
142893
|
"name": "identifier",
|
|
@@ -142300,7 +142951,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142300
142951
|
"/service/dtm/definitions/measurements/count": { "get": {
|
|
142301
142952
|
"tags": ["Measurement Definitions"],
|
|
142302
142953
|
"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.*",
|
|
142954
|
+
"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
142955
|
"operationId": "countMeasurementDefinitions",
|
|
142305
142956
|
"parameters": [
|
|
142306
142957
|
{
|
|
@@ -142343,7 +142994,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142343
142994
|
"get": {
|
|
142344
142995
|
"tags": ["Event Definitions"],
|
|
142345
142996
|
"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`.",
|
|
142997
|
+
"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
142998
|
"operationId": "getEventDefinition",
|
|
142348
142999
|
"parameters": [{
|
|
142349
143000
|
"name": "identifier",
|
|
@@ -142407,7 +143058,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142407
143058
|
"/service/dtm/definitions/events/count": { "get": {
|
|
142408
143059
|
"tags": ["Event Definitions"],
|
|
142409
143060
|
"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.*",
|
|
143061
|
+
"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
143062
|
"operationId": "countEventDefinitions",
|
|
142412
143063
|
"parameters": [
|
|
142413
143064
|
{
|
|
@@ -142450,7 +143101,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142450
143101
|
"get": {
|
|
142451
143102
|
"tags": ["Asset Definitions"],
|
|
142452
143103
|
"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`.",
|
|
143104
|
+
"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
143105
|
"operationId": "getAssetDefinition",
|
|
142455
143106
|
"parameters": [{
|
|
142456
143107
|
"name": "identifier",
|
|
@@ -142482,7 +143133,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142482
143133
|
"/service/dtm/definitions/assets/count": { "get": {
|
|
142483
143134
|
"tags": ["Asset Definitions"],
|
|
142484
143135
|
"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.*",
|
|
143136
|
+
"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
143137
|
"operationId": "countAssetDefinitions",
|
|
142487
143138
|
"parameters": [
|
|
142488
143139
|
{
|
|
@@ -142534,7 +143185,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142534
143185
|
"get": {
|
|
142535
143186
|
"tags": ["Alarm Definitions"],
|
|
142536
143187
|
"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`.",
|
|
143188
|
+
"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
143189
|
"operationId": "getAlarmDefinition",
|
|
142539
143190
|
"parameters": [{
|
|
142540
143191
|
"name": "identifier",
|
|
@@ -142598,7 +143249,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142598
143249
|
"/service/dtm/definitions/alarms/count": { "get": {
|
|
142599
143250
|
"tags": ["Alarm Definitions"],
|
|
142600
143251
|
"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.*",
|
|
143252
|
+
"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
143253
|
"operationId": "countAlarmDefinitions",
|
|
142603
143254
|
"parameters": [
|
|
142604
143255
|
{
|
|
@@ -142637,11 +143288,41 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142637
143288
|
"content": { "application/json": { "schema": { "type": "integer" } } }
|
|
142638
143289
|
} }
|
|
142639
143290
|
} },
|
|
142640
|
-
"/service/dtm/assets/{assetId}/
|
|
143291
|
+
"/service/dtm/assets/{assetId}/linkedSeries/count": { "get": {
|
|
143292
|
+
"tags": ["Linked Series"],
|
|
143293
|
+
"summary": "Get count of linked series for an asset",
|
|
143294
|
+
"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. ",
|
|
143295
|
+
"operationId": "countLinkedSeries",
|
|
143296
|
+
"parameters": [
|
|
143297
|
+
{
|
|
143298
|
+
"name": "assetId",
|
|
143299
|
+
"in": "path",
|
|
143300
|
+
"required": true,
|
|
143301
|
+
"schema": { "type": "string" }
|
|
143302
|
+
},
|
|
143303
|
+
{
|
|
143304
|
+
"name": "fragment",
|
|
143305
|
+
"in": "query",
|
|
143306
|
+
"description": "A characteristic which identifies the measurement.",
|
|
143307
|
+
"schema": { "type": "string" }
|
|
143308
|
+
},
|
|
143309
|
+
{
|
|
143310
|
+
"name": "series",
|
|
143311
|
+
"in": "query",
|
|
143312
|
+
"description": "The specific series to search for.",
|
|
143313
|
+
"schema": { "type": "string" }
|
|
143314
|
+
}
|
|
143315
|
+
],
|
|
143316
|
+
"responses": { "200": {
|
|
143317
|
+
"description": "OK",
|
|
143318
|
+
"content": { "application/json": { "schema": { "type": "integer" } } }
|
|
143319
|
+
} }
|
|
143320
|
+
} },
|
|
143321
|
+
"/service/dtm/assets/{assetId}/devices": { "get": {
|
|
142641
143322
|
"tags": ["Assets"],
|
|
142642
|
-
"summary": "Retrieve
|
|
142643
|
-
"description": "Retrieves
|
|
142644
|
-
"operationId": "
|
|
143323
|
+
"summary": "Retrieve child devices of an asset",
|
|
143324
|
+
"description": "Retrieves child devices of an asset.\n\n Fetches all child devices of the parent asset specified by `assetId`. ",
|
|
143325
|
+
"operationId": "getChildDevices",
|
|
142645
143326
|
"parameters": [
|
|
142646
143327
|
{
|
|
142647
143328
|
"name": "assetId",
|
|
@@ -142672,22 +143353,12 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142672
143353
|
}
|
|
142673
143354
|
}
|
|
142674
143355
|
},
|
|
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
143356
|
{
|
|
142685
143357
|
"name": "currentPage",
|
|
142686
143358
|
"in": "query",
|
|
142687
143359
|
"schema": {
|
|
142688
|
-
"
|
|
142689
|
-
"
|
|
142690
|
-
"default": 1
|
|
143360
|
+
"minimum": 1,
|
|
143361
|
+
"type": "integer"
|
|
142691
143362
|
}
|
|
142692
143363
|
},
|
|
142693
143364
|
{
|
|
@@ -142702,50 +143373,26 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142702
143373
|
],
|
|
142703
143374
|
"responses": { "200": {
|
|
142704
143375
|
"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" } } }
|
|
143376
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedDeviceList" } } }
|
|
142736
143377
|
} }
|
|
142737
143378
|
} },
|
|
142738
|
-
"/service/dtm/assets/
|
|
143379
|
+
"/service/dtm/assets/subAssets": { "get": {
|
|
142739
143380
|
"tags": ["Assets"],
|
|
142740
|
-
"summary": "Retrieve
|
|
142741
|
-
"description": "Retrieves
|
|
142742
|
-
"operationId": "
|
|
143381
|
+
"summary": "Retrieve sub-assets by parent IDs",
|
|
143382
|
+
"description": "Retrieves sub-assets across multiple parent assets.\n\n Fetches all direct sub-assets that are children of given parent asset IDs. ",
|
|
143383
|
+
"operationId": "getSubAssetsByParents",
|
|
142743
143384
|
"parameters": [
|
|
142744
143385
|
{
|
|
142745
|
-
"name": "
|
|
142746
|
-
"in": "
|
|
143386
|
+
"name": "parents",
|
|
143387
|
+
"in": "query",
|
|
143388
|
+
"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
143389
|
"required": true,
|
|
142748
|
-
"
|
|
143390
|
+
"explode": false,
|
|
143391
|
+
"schema": {
|
|
143392
|
+
"minItems": 1,
|
|
143393
|
+
"type": "array",
|
|
143394
|
+
"items": { "type": "string" }
|
|
143395
|
+
}
|
|
142749
143396
|
},
|
|
142750
143397
|
{
|
|
142751
143398
|
"name": "query",
|
|
@@ -142770,13 +143417,48 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142770
143417
|
}
|
|
142771
143418
|
}
|
|
142772
143419
|
},
|
|
143420
|
+
{
|
|
143421
|
+
"name": "withLinkedSeries",
|
|
143422
|
+
"in": "query",
|
|
143423
|
+
"description": "Indicates the resource to add the `c8y_LinkedSeries` fragment in the response.",
|
|
143424
|
+
"schema": {
|
|
143425
|
+
"type": "boolean",
|
|
143426
|
+
"default": false
|
|
143427
|
+
}
|
|
143428
|
+
},
|
|
143429
|
+
{
|
|
143430
|
+
"name": "withParents",
|
|
143431
|
+
"in": "query",
|
|
143432
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
143433
|
+
"schema": {
|
|
143434
|
+
"type": "boolean",
|
|
143435
|
+
"default": false
|
|
143436
|
+
}
|
|
143437
|
+
},
|
|
143438
|
+
{
|
|
143439
|
+
"name": "includeGroups",
|
|
143440
|
+
"in": "query",
|
|
143441
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
143442
|
+
"schema": {
|
|
143443
|
+
"type": "boolean",
|
|
143444
|
+
"default": false
|
|
143445
|
+
}
|
|
143446
|
+
},
|
|
143447
|
+
{
|
|
143448
|
+
"name": "withChildrenCount",
|
|
143449
|
+
"in": "query",
|
|
143450
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
143451
|
+
"schema": {
|
|
143452
|
+
"type": "boolean",
|
|
143453
|
+
"default": false
|
|
143454
|
+
}
|
|
143455
|
+
},
|
|
142773
143456
|
{
|
|
142774
143457
|
"name": "currentPage",
|
|
142775
143458
|
"in": "query",
|
|
142776
143459
|
"schema": {
|
|
142777
|
-
"
|
|
142778
|
-
"
|
|
142779
|
-
"default": 1
|
|
143460
|
+
"minimum": 1,
|
|
143461
|
+
"type": "integer"
|
|
142780
143462
|
}
|
|
142781
143463
|
},
|
|
142782
143464
|
{
|
|
@@ -142791,13 +143473,13 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142791
143473
|
],
|
|
142792
143474
|
"responses": { "200": {
|
|
142793
143475
|
"description": "OK",
|
|
142794
|
-
"content": { "application/json": { "schema": { "
|
|
143476
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedAssetList" } } }
|
|
142795
143477
|
} }
|
|
142796
143478
|
} },
|
|
142797
143479
|
"/service/dtm/assets/linkedSeries": { "get": {
|
|
142798
143480
|
"tags": ["Linked Series"],
|
|
142799
143481
|
"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.",
|
|
143482
|
+
"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
143483
|
"operationId": "getLinkedSeriesByQuery",
|
|
142802
143484
|
"parameters": [
|
|
142803
143485
|
{
|
|
@@ -142832,9 +143514,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142832
143514
|
"name": "currentPage",
|
|
142833
143515
|
"in": "query",
|
|
142834
143516
|
"schema": {
|
|
142835
|
-
"
|
|
142836
|
-
"
|
|
142837
|
-
"default": 1
|
|
143517
|
+
"minimum": 1,
|
|
143518
|
+
"type": "integer"
|
|
142838
143519
|
}
|
|
142839
143520
|
},
|
|
142840
143521
|
{
|
|
@@ -142852,15 +143533,66 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142852
143533
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedAssetList" } } }
|
|
142853
143534
|
} }
|
|
142854
143535
|
} },
|
|
143536
|
+
"/service/dtm/assets/linkedSeries/opposites/{deviceId}": { "get": {
|
|
143537
|
+
"tags": ["Linked Series"],
|
|
143538
|
+
"summary": "Get all assets linked to a specific device by its ID.",
|
|
143539
|
+
"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.",
|
|
143540
|
+
"operationId": "getOppositeAssets",
|
|
143541
|
+
"parameters": [
|
|
143542
|
+
{
|
|
143543
|
+
"name": "deviceId",
|
|
143544
|
+
"in": "path",
|
|
143545
|
+
"description": "the ID of the device",
|
|
143546
|
+
"required": true,
|
|
143547
|
+
"schema": { "type": "string" }
|
|
143548
|
+
},
|
|
143549
|
+
{
|
|
143550
|
+
"name": "assetIds",
|
|
143551
|
+
"in": "query",
|
|
143552
|
+
"description": "The asset IDs to search for.",
|
|
143553
|
+
"explode": false,
|
|
143554
|
+
"schema": {
|
|
143555
|
+
"type": "array",
|
|
143556
|
+
"items": { "type": "string" }
|
|
143557
|
+
}
|
|
143558
|
+
},
|
|
143559
|
+
{
|
|
143560
|
+
"name": "fragment",
|
|
143561
|
+
"in": "query",
|
|
143562
|
+
"description": "A characteristic which identifies the measurement.",
|
|
143563
|
+
"schema": { "type": "string" }
|
|
143564
|
+
},
|
|
143565
|
+
{
|
|
143566
|
+
"name": "series",
|
|
143567
|
+
"in": "query",
|
|
143568
|
+
"description": "The specific series to search for.",
|
|
143569
|
+
"schema": { "type": "string" }
|
|
143570
|
+
}
|
|
143571
|
+
],
|
|
143572
|
+
"responses": { "200": {
|
|
143573
|
+
"description": "a set of assets linked to the specified device",
|
|
143574
|
+
"content": {
|
|
143575
|
+
"application/vnd.com.nsn.cumulocity.linkedassetscollection+json": { "schema": {
|
|
143576
|
+
"type": "array",
|
|
143577
|
+
"items": { "$ref": "#/components/schemas/LinkedAsset" }
|
|
143578
|
+
} },
|
|
143579
|
+
"application/json": { "schema": {
|
|
143580
|
+
"type": "array",
|
|
143581
|
+
"items": { "$ref": "#/components/schemas/Asset" }
|
|
143582
|
+
} }
|
|
143583
|
+
}
|
|
143584
|
+
} }
|
|
143585
|
+
} },
|
|
142855
143586
|
"/service/dtm/assets/externalIds/{externalId}": { "get": {
|
|
142856
143587
|
"tags": ["Assets"],
|
|
142857
143588
|
"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.",
|
|
143589
|
+
"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
143590
|
"operationId": "getAssetByExternalId",
|
|
142860
143591
|
"parameters": [
|
|
142861
143592
|
{
|
|
142862
143593
|
"name": "externalId",
|
|
142863
143594
|
"in": "path",
|
|
143595
|
+
"description": "The external identifier of type `c8y_Asset` of the asset.",
|
|
142864
143596
|
"required": true,
|
|
142865
143597
|
"schema": { "type": "string" }
|
|
142866
143598
|
},
|
|
@@ -142876,7 +143608,26 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142876
143608
|
{
|
|
142877
143609
|
"name": "withSubAssets",
|
|
142878
143610
|
"in": "query",
|
|
142879
|
-
"description": "Indicates the resource to add the sub-assets in the response.",
|
|
143611
|
+
"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.",
|
|
143612
|
+
"deprecated": true,
|
|
143613
|
+
"schema": {
|
|
143614
|
+
"type": "boolean",
|
|
143615
|
+
"default": false
|
|
143616
|
+
}
|
|
143617
|
+
},
|
|
143618
|
+
{
|
|
143619
|
+
"name": "withParents",
|
|
143620
|
+
"in": "query",
|
|
143621
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
143622
|
+
"schema": {
|
|
143623
|
+
"type": "boolean",
|
|
143624
|
+
"default": false
|
|
143625
|
+
}
|
|
143626
|
+
},
|
|
143627
|
+
{
|
|
143628
|
+
"name": "withChildrenCount",
|
|
143629
|
+
"in": "query",
|
|
143630
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
142880
143631
|
"schema": {
|
|
142881
143632
|
"type": "boolean",
|
|
142882
143633
|
"default": false
|
|
@@ -142888,41 +143639,148 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142888
143639
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Asset" } } }
|
|
142889
143640
|
} }
|
|
142890
143641
|
} },
|
|
143642
|
+
"/service/dtm/assets/externalIds/{externalId}/subAssets": { "get": {
|
|
143643
|
+
"tags": ["Assets"],
|
|
143644
|
+
"summary": "Retrieve sub-assets of an asset by its external ID",
|
|
143645
|
+
"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. ",
|
|
143646
|
+
"operationId": "getSubAssetsByExternalId",
|
|
143647
|
+
"parameters": [
|
|
143648
|
+
{
|
|
143649
|
+
"name": "externalId",
|
|
143650
|
+
"in": "path",
|
|
143651
|
+
"description": "The external identifier of type `c8y_Asset` of the asset.",
|
|
143652
|
+
"required": true,
|
|
143653
|
+
"schema": { "type": "string" }
|
|
143654
|
+
},
|
|
143655
|
+
{
|
|
143656
|
+
"name": "query",
|
|
143657
|
+
"in": "query",
|
|
143658
|
+
"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).",
|
|
143659
|
+
"schema": {
|
|
143660
|
+
"type": "string",
|
|
143661
|
+
"format": "c8y:query"
|
|
143662
|
+
},
|
|
143663
|
+
"examples": {
|
|
143664
|
+
"Filter by name": {
|
|
143665
|
+
"description": "Filter by name",
|
|
143666
|
+
"value": "$filter=name eq 'Windfarm'"
|
|
143667
|
+
},
|
|
143668
|
+
"Filter by type": {
|
|
143669
|
+
"description": "Filter by type",
|
|
143670
|
+
"value": "$filter=type eq 'c8y_*'"
|
|
143671
|
+
},
|
|
143672
|
+
"Using orderby": {
|
|
143673
|
+
"description": "Using orderby",
|
|
143674
|
+
"value": "$orderby=id asc"
|
|
143675
|
+
}
|
|
143676
|
+
}
|
|
143677
|
+
},
|
|
143678
|
+
{
|
|
143679
|
+
"name": "withLinkedSeries",
|
|
143680
|
+
"in": "query",
|
|
143681
|
+
"description": "Indicates the resource to add the `c8y_LinkedSeries` fragment in the response.",
|
|
143682
|
+
"schema": {
|
|
143683
|
+
"type": "boolean",
|
|
143684
|
+
"default": false
|
|
143685
|
+
}
|
|
143686
|
+
},
|
|
143687
|
+
{
|
|
143688
|
+
"name": "withParents",
|
|
143689
|
+
"in": "query",
|
|
143690
|
+
"description": "Indicates the resource to add the parent assets in the response.",
|
|
143691
|
+
"schema": {
|
|
143692
|
+
"type": "boolean",
|
|
143693
|
+
"default": false
|
|
143694
|
+
}
|
|
143695
|
+
},
|
|
143696
|
+
{
|
|
143697
|
+
"name": "includeGroups",
|
|
143698
|
+
"in": "query",
|
|
143699
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
143700
|
+
"schema": {
|
|
143701
|
+
"type": "boolean",
|
|
143702
|
+
"default": false
|
|
143703
|
+
}
|
|
143704
|
+
},
|
|
143705
|
+
{
|
|
143706
|
+
"name": "withChildrenCount",
|
|
143707
|
+
"in": "query",
|
|
143708
|
+
"description": "Indicates the resource to include the total number of child entities (sub-assets and devices) in the response.",
|
|
143709
|
+
"schema": {
|
|
143710
|
+
"type": "boolean",
|
|
143711
|
+
"default": false
|
|
143712
|
+
}
|
|
143713
|
+
},
|
|
143714
|
+
{
|
|
143715
|
+
"name": "currentPage",
|
|
143716
|
+
"in": "query",
|
|
143717
|
+
"schema": {
|
|
143718
|
+
"minimum": 1,
|
|
143719
|
+
"type": "integer"
|
|
143720
|
+
}
|
|
143721
|
+
},
|
|
143722
|
+
{
|
|
143723
|
+
"name": "pageSize",
|
|
143724
|
+
"in": "query",
|
|
143725
|
+
"schema": {
|
|
143726
|
+
"maximum": 2e3,
|
|
143727
|
+
"minimum": 1,
|
|
143728
|
+
"type": "integer"
|
|
143729
|
+
}
|
|
143730
|
+
}
|
|
143731
|
+
],
|
|
143732
|
+
"responses": { "200": {
|
|
143733
|
+
"description": "OK",
|
|
143734
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaginatedAssetList" } } }
|
|
143735
|
+
} }
|
|
143736
|
+
} },
|
|
142891
143737
|
"/service/dtm/assets/count": { "get": {
|
|
142892
143738
|
"tags": ["Assets"],
|
|
142893
143739
|
"summary": "Get count of assets",
|
|
142894
|
-
"description": "Counts all assets on your tenant.\n\n You can optionally apply the filters `query` and `
|
|
143740
|
+
"description": "Counts all assets on your tenant.\n\n You can optionally apply the filters `query`, `onlyRoots` and `includeGroups`. ",
|
|
142895
143741
|
"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_*'"
|
|
143742
|
+
"parameters": [
|
|
143743
|
+
{
|
|
143744
|
+
"name": "query",
|
|
143745
|
+
"in": "query",
|
|
143746
|
+
"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).",
|
|
143747
|
+
"schema": {
|
|
143748
|
+
"type": "string",
|
|
143749
|
+
"format": "c8y:query"
|
|
142912
143750
|
},
|
|
142913
|
-
"
|
|
142914
|
-
"
|
|
142915
|
-
|
|
143751
|
+
"examples": {
|
|
143752
|
+
"Filter by name": {
|
|
143753
|
+
"description": "Filter by name",
|
|
143754
|
+
"value": "$filter=name eq 'Windfarm'"
|
|
143755
|
+
},
|
|
143756
|
+
"Filter by type": {
|
|
143757
|
+
"description": "Filter by type",
|
|
143758
|
+
"value": "$filter=type eq 'c8y_*'"
|
|
143759
|
+
},
|
|
143760
|
+
"Using orderby": {
|
|
143761
|
+
"description": "Using orderby",
|
|
143762
|
+
"value": "$orderby=id asc"
|
|
143763
|
+
}
|
|
143764
|
+
}
|
|
143765
|
+
},
|
|
143766
|
+
{
|
|
143767
|
+
"name": "onlyRoots",
|
|
143768
|
+
"in": "query",
|
|
143769
|
+
"schema": {
|
|
143770
|
+
"type": "boolean",
|
|
143771
|
+
"default": false
|
|
143772
|
+
}
|
|
143773
|
+
},
|
|
143774
|
+
{
|
|
143775
|
+
"name": "includeGroups",
|
|
143776
|
+
"in": "query",
|
|
143777
|
+
"description": "Indicates the resource to include assets and device groups in the response. If `false`, only assets are included.",
|
|
143778
|
+
"schema": {
|
|
143779
|
+
"type": "boolean",
|
|
143780
|
+
"default": false
|
|
142916
143781
|
}
|
|
142917
143782
|
}
|
|
142918
|
-
|
|
142919
|
-
"name": "onlyRoots",
|
|
142920
|
-
"in": "query",
|
|
142921
|
-
"schema": {
|
|
142922
|
-
"type": "boolean",
|
|
142923
|
-
"default": false
|
|
142924
|
-
}
|
|
142925
|
-
}],
|
|
143783
|
+
],
|
|
142926
143784
|
"responses": { "200": {
|
|
142927
143785
|
"description": "OK",
|
|
142928
143786
|
"content": { "application/json": { "schema": { "type": "integer" } } }
|
|
@@ -142949,7 +143807,11 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
142949
143807
|
"pattern": "^(?:[A-Za-z0-9](?:\\s*[-\\w:.!])*\\s*)?$",
|
|
142950
143808
|
"type": "string"
|
|
142951
143809
|
},
|
|
142952
|
-
"description": { "type": "string" }
|
|
143810
|
+
"description": { "type": "string" },
|
|
143811
|
+
"additionalProperties": {
|
|
143812
|
+
"type": "boolean",
|
|
143813
|
+
"writeOnly": true
|
|
143814
|
+
}
|
|
142953
143815
|
},
|
|
142954
143816
|
"example": {
|
|
142955
143817
|
"title": "Position",
|
|
@@ -143010,12 +143872,20 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143010
143872
|
}
|
|
143011
143873
|
},
|
|
143012
143874
|
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
143875
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" },
|
|
143013
143876
|
"c8y_Origin": {
|
|
143014
143877
|
"type": "string",
|
|
143015
143878
|
"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
143879
|
}
|
|
143017
143880
|
}
|
|
143018
143881
|
},
|
|
143882
|
+
"SharedDefinition": {
|
|
143883
|
+
"type": "object",
|
|
143884
|
+
"properties": { "self": { "type": "string" } },
|
|
143885
|
+
"additionalProperties": false,
|
|
143886
|
+
"description": "Indicates that this definition is maintained on the enterprise tenant and is shared on its subtenants.",
|
|
143887
|
+
"readOnly": true
|
|
143888
|
+
},
|
|
143019
143889
|
"AllowedPropertyDefinition": {
|
|
143020
143890
|
"required": ["identifier"],
|
|
143021
143891
|
"type": "object",
|
|
@@ -143072,7 +143942,8 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143072
143942
|
} },
|
|
143073
143943
|
"additionalProperties": false
|
|
143074
143944
|
},
|
|
143075
|
-
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" }
|
|
143945
|
+
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
143946
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" }
|
|
143076
143947
|
}
|
|
143077
143948
|
},
|
|
143078
143949
|
"EventDefinition": {
|
|
@@ -143105,6 +143976,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143105
143976
|
}
|
|
143106
143977
|
},
|
|
143107
143978
|
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
143979
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" },
|
|
143108
143980
|
"composition": {
|
|
143109
143981
|
"type": "object",
|
|
143110
143982
|
"properties": { "allowedProperties": {
|
|
@@ -143146,6 +144018,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143146
144018
|
}
|
|
143147
144019
|
},
|
|
143148
144020
|
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
144021
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" },
|
|
143149
144022
|
"composition": {
|
|
143150
144023
|
"type": "object",
|
|
143151
144024
|
"properties": {
|
|
@@ -143202,6 +144075,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143202
144075
|
}
|
|
143203
144076
|
},
|
|
143204
144077
|
"c8y_AvailableActions": { "$ref": "#/components/schemas/AvailableActions" },
|
|
144078
|
+
"c8y_SharedDefinition": { "$ref": "#/components/schemas/SharedDefinition" },
|
|
143205
144079
|
"composition": {
|
|
143206
144080
|
"type": "object",
|
|
143207
144081
|
"properties": { "allowedProperties": {
|
|
@@ -143246,6 +144120,11 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143246
144120
|
"readOnly": true,
|
|
143247
144121
|
"items": { "$ref": "#/components/schemas/Asset" }
|
|
143248
144122
|
},
|
|
144123
|
+
"assetParents": {
|
|
144124
|
+
"type": "array",
|
|
144125
|
+
"readOnly": true,
|
|
144126
|
+
"items": { "$ref": "#/components/schemas/Asset" }
|
|
144127
|
+
},
|
|
143249
144128
|
"c8y_LinkedSeries": {
|
|
143250
144129
|
"type": "array",
|
|
143251
144130
|
"items": { "$ref": "#/components/schemas/LinkedSeries" }
|
|
@@ -143256,7 +144135,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143256
144135
|
},
|
|
143257
144136
|
"c8y_LatestMeasurements": { "$ref": "#/components/schemas/LatestMeasurements" },
|
|
143258
144137
|
"c8y_ExternalId": {
|
|
143259
|
-
"maxLength":
|
|
144138
|
+
"maxLength": 680,
|
|
143260
144139
|
"minLength": 1,
|
|
143261
144140
|
"type": "string",
|
|
143262
144141
|
"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 +144143,11 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143264
144143
|
"c8y_ExternalAsset": {
|
|
143265
144144
|
"type": "object",
|
|
143266
144145
|
"description": "Represents Assets synchronized from external systems into Cumulocity IoT. The fragment allows storing free-form metadata relevant to external systems."
|
|
144146
|
+
},
|
|
144147
|
+
"assignedChildrenCount": {
|
|
144148
|
+
"type": "integer",
|
|
144149
|
+
"format": "int32",
|
|
144150
|
+
"readOnly": true
|
|
143267
144151
|
}
|
|
143268
144152
|
}
|
|
143269
144153
|
},
|
|
@@ -143343,6 +144227,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143343
144227
|
"example": "T"
|
|
143344
144228
|
},
|
|
143345
144229
|
"id": {
|
|
144230
|
+
"pattern": "^(?!\\s*$).+$",
|
|
143346
144231
|
"type": "string",
|
|
143347
144232
|
"example": "9688123"
|
|
143348
144233
|
},
|
|
@@ -143357,6 +144242,51 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143357
144242
|
},
|
|
143358
144243
|
"additionalProperties": false
|
|
143359
144244
|
},
|
|
144245
|
+
"AssetReference": {
|
|
144246
|
+
"type": "object",
|
|
144247
|
+
"properties": {
|
|
144248
|
+
"fragment": {
|
|
144249
|
+
"type": "string",
|
|
144250
|
+
"example": "c8y_Temperature"
|
|
144251
|
+
},
|
|
144252
|
+
"series": {
|
|
144253
|
+
"type": "string",
|
|
144254
|
+
"example": "T"
|
|
144255
|
+
},
|
|
144256
|
+
"id": {
|
|
144257
|
+
"type": "string",
|
|
144258
|
+
"example": "9688123"
|
|
144259
|
+
},
|
|
144260
|
+
"label": {
|
|
144261
|
+
"type": "string",
|
|
144262
|
+
"example": "Room temperature"
|
|
144263
|
+
}
|
|
144264
|
+
},
|
|
144265
|
+
"additionalProperties": false
|
|
144266
|
+
},
|
|
144267
|
+
"LinkedAsset": {
|
|
144268
|
+
"type": "object",
|
|
144269
|
+
"properties": {
|
|
144270
|
+
"fragment": {
|
|
144271
|
+
"type": "string",
|
|
144272
|
+
"example": "c8y_Temperature"
|
|
144273
|
+
},
|
|
144274
|
+
"series": {
|
|
144275
|
+
"type": "string",
|
|
144276
|
+
"example": "T"
|
|
144277
|
+
},
|
|
144278
|
+
"type": {
|
|
144279
|
+
"type": "string",
|
|
144280
|
+
"example": "c8y_TemperatureMeasurement"
|
|
144281
|
+
},
|
|
144282
|
+
"name": {
|
|
144283
|
+
"type": "string",
|
|
144284
|
+
"example": "MyTemperatureMeasurement"
|
|
144285
|
+
},
|
|
144286
|
+
"asset": { "$ref": "#/components/schemas/AssetReference" }
|
|
144287
|
+
},
|
|
144288
|
+
"additionalProperties": false
|
|
144289
|
+
},
|
|
143360
144290
|
"PageStatistics": {
|
|
143361
144291
|
"required": [
|
|
143362
144292
|
"currentPage",
|
|
@@ -143392,7 +144322,7 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143392
144322
|
"additionalProperties": false
|
|
143393
144323
|
},
|
|
143394
144324
|
"PaginatedDefinitionList": {
|
|
143395
|
-
"required": ["
|
|
144325
|
+
"required": ["statistics"],
|
|
143396
144326
|
"type": "object",
|
|
143397
144327
|
"properties": {
|
|
143398
144328
|
"self": {
|
|
@@ -143497,6 +144427,71 @@ const BUNDLED_SERVICE_SPECS = Object.freeze([{
|
|
|
143497
144427
|
},
|
|
143498
144428
|
"additionalProperties": false
|
|
143499
144429
|
},
|
|
144430
|
+
"Device": {
|
|
144431
|
+
"title": "Device",
|
|
144432
|
+
"required": ["id"],
|
|
144433
|
+
"type": "object",
|
|
144434
|
+
"properties": {
|
|
144435
|
+
"creationTime": {
|
|
144436
|
+
"type": "string",
|
|
144437
|
+
"readOnly": true,
|
|
144438
|
+
"example": "2017-12-12T22:09:06.881+01:00"
|
|
144439
|
+
},
|
|
144440
|
+
"lastUpdated": {
|
|
144441
|
+
"type": "string",
|
|
144442
|
+
"readOnly": true,
|
|
144443
|
+
"example": "2018-07-19T12:01:50.731Z"
|
|
144444
|
+
},
|
|
144445
|
+
"id": {
|
|
144446
|
+
"type": "string",
|
|
144447
|
+
"readOnly": true,
|
|
144448
|
+
"example": "4512412"
|
|
144449
|
+
},
|
|
144450
|
+
"type": {
|
|
144451
|
+
"type": "string",
|
|
144452
|
+
"example": "c8y_TemperatureSensor"
|
|
144453
|
+
},
|
|
144454
|
+
"name": {
|
|
144455
|
+
"type": "string",
|
|
144456
|
+
"example": "TemperatureSensor"
|
|
144457
|
+
},
|
|
144458
|
+
"owner": { "type": "string" },
|
|
144459
|
+
"c8y_LatestMeasurements": { "$ref": "#/components/schemas/LatestMeasurements" }
|
|
144460
|
+
}
|
|
144461
|
+
},
|
|
144462
|
+
"PaginatedDeviceList": {
|
|
144463
|
+
"required": ["devices", "statistics"],
|
|
144464
|
+
"type": "object",
|
|
144465
|
+
"properties": {
|
|
144466
|
+
"self": {
|
|
144467
|
+
"title": "SelfURL",
|
|
144468
|
+
"type": "string",
|
|
144469
|
+
"description": "A URL linking to this resource.",
|
|
144470
|
+
"format": "uri",
|
|
144471
|
+
"readOnly": true
|
|
144472
|
+
},
|
|
144473
|
+
"next": {
|
|
144474
|
+
"title": "NextPageURL",
|
|
144475
|
+
"type": "string",
|
|
144476
|
+
"description": "A URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] to a potential next page.",
|
|
144477
|
+
"format": "uri",
|
|
144478
|
+
"readOnly": true
|
|
144479
|
+
},
|
|
144480
|
+
"prev": {
|
|
144481
|
+
"title": "PreviousPageURL",
|
|
144482
|
+
"type": "string",
|
|
144483
|
+
"description": "A URI reference [[RFC3986](https://tools.ietf.org/html/rfc3986)] to a potential previous page.",
|
|
144484
|
+
"format": "uri",
|
|
144485
|
+
"readOnly": true
|
|
144486
|
+
},
|
|
144487
|
+
"devices": {
|
|
144488
|
+
"type": "array",
|
|
144489
|
+
"items": { "$ref": "#/components/schemas/Device" }
|
|
144490
|
+
},
|
|
144491
|
+
"statistics": { "$ref": "#/components/schemas/PageStatistics" }
|
|
144492
|
+
},
|
|
144493
|
+
"additionalProperties": false
|
|
144494
|
+
},
|
|
143500
144495
|
"AllowedSubAssetDefinition": {
|
|
143501
144496
|
"required": ["identifier"],
|
|
143502
144497
|
"type": "object",
|
|
@@ -143898,6 +144893,10 @@ runMain(defineCommand({
|
|
|
143898
144893
|
"no-mcp": {
|
|
143899
144894
|
type: "string",
|
|
143900
144895
|
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."
|
|
144896
|
+
},
|
|
144897
|
+
"mcp-server": {
|
|
144898
|
+
type: "string",
|
|
144899
|
+
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
144900
|
}
|
|
143902
144901
|
},
|
|
143903
144902
|
setup: () => {
|
|
@@ -143922,6 +144921,10 @@ runMain(defineCommand({
|
|
|
143922
144921
|
const rawNoMcp = args["no-mcp"];
|
|
143923
144922
|
const noMcp = parseNoMcp(Array.isArray(rawNoMcp) ? rawNoMcp : rawNoMcp !== void 0 ? [rawNoMcp] : []);
|
|
143924
144923
|
if (noMcp.all || noMcp.contextPaths.size > 0) consola.info(`MCP wrapping disabled for: ${noMcp.all ? "all services" : [...noMcp.contextPaths].join(", ")}`);
|
|
144924
|
+
const rawMcpServers = args["mcp-server"];
|
|
144925
|
+
const { servers: externalMcpServers, failedEntries: failedMcpServers } = parseExternalMcpServers((Array.isArray(rawMcpServers) ? rawMcpServers : rawMcpServers ? [rawMcpServers] : []).filter((v) => typeof v === "string" && v.length > 0));
|
|
144926
|
+
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"));
|
|
144927
|
+
if (externalMcpServers.length > 0) consola.info(`External MCP namespaces: ${externalMcpServers.map((s) => `${s.name} (${s.url})`).join(", ")}`);
|
|
143925
144928
|
const activeTenant = readActiveTenantUrl();
|
|
143926
144929
|
if (activeTenant) try {
|
|
143927
144930
|
const tenantCtx = await setCliTenantContext(activeTenant);
|
|
@@ -143946,6 +144949,7 @@ runMain(defineCommand({
|
|
|
143946
144949
|
restrictions,
|
|
143947
144950
|
allowRules: parsedAllowRules,
|
|
143948
144951
|
noMcp,
|
|
144952
|
+
externalMcpServers,
|
|
143949
144953
|
specs: active?.specs ?? getBundledOnlyCapabilities(),
|
|
143950
144954
|
auth: active ? {
|
|
143951
144955
|
tenantUrl: active.tenantUrl,
|