@vectros-ai/mcp-server 0.12.0 → 0.13.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/CHANGELOG.md +37 -0
- package/README.md +2 -2
- package/dist/cli-http.js +160 -34
- package/dist/cli-http.js.map +1 -1
- package/dist/cli-http.mjs +160 -34
- package/dist/cli-http.mjs.map +1 -1
- package/dist/cli.js +160 -34
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +160 -34
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +160 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +160 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/cli-http.mjs
CHANGED
|
@@ -2432,7 +2432,7 @@ var AuthClient = class {
|
|
|
2432
2432
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/admin/access-log");
|
|
2433
2433
|
}
|
|
2434
2434
|
/**
|
|
2435
|
-
* Lists
|
|
2435
|
+
* Lists your scoped API keys (`ssk_*`) in your credential's own environment — a live key lists live keys, a test key lists test keys. Revoked keys are excluded. Requires the `keys:r` scope.
|
|
2436
2436
|
*
|
|
2437
2437
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
2438
2438
|
*
|
|
@@ -2815,7 +2815,7 @@ var AuthClient = class {
|
|
|
2815
2815
|
);
|
|
2816
2816
|
}
|
|
2817
2817
|
/**
|
|
2818
|
-
* Creates a new access profile under the given app context. This call is idempotent by `principalId`: if a profile with the same `principalId` already exists, the existing profile is returned (with status 200) instead of creating a duplicate. The response's `created` field (and the HTTP status — 201 when created, 200 when an existing profile was returned) tells the two apart. To overwrite an existing profile's `scopes`/`roleId`, `identityOverrides`, and `status` instead of returning it unchanged, set `?upsert=true` (this also requires the `profiles:u` scope). You must provide exactly one of `scopes` (an inline list of scopes) or `roleId` (a reference to a role); supplying both, or neither, is rejected. `identityOverrides` is keyed by ownership namespace in `scope:<namespace>` form — `scope:org` and `scope:client` for the reserved namespaces, or any namespace you have registered — and may name at most two; any other key (including the account identifier or `userId`) is rejected. If you use a scoped credential, the profile's effective scopes may not exceed your own; a root API key (`sk_`) is exempt. Requires the `profiles:c` scope.
|
|
2818
|
+
* Creates a new access profile under the given app context. This call is idempotent by `principalId`: if a profile with the same `principalId` already exists, the existing profile is returned (with status 200) instead of creating a duplicate. The response's `created` field (and the HTTP status — 201 when created, 200 when an existing profile was returned) tells the two apart. To overwrite an existing profile's `scopes`/`roleId`, `identityOverrides`, and `status` instead of returning it unchanged, set `?upsert=true` (this also requires the `profiles:u` scope, and applies the same `identityOverrides` bounds the update endpoint documents — a scoped credential may not repoint or clear an identity value it does not itself hold). You must provide exactly one of `scopes` (an inline list of scopes) or `roleId` (a reference to a role); supplying both, or neither, is rejected. `identityOverrides` is keyed by ownership namespace in `scope:<namespace>` form — `scope:org` and `scope:client` for the reserved namespaces, or any namespace you have registered — and may name at most two; any other key (including the account identifier or `userId`) is rejected. If you use a scoped credential, the profile's effective scopes may not exceed your own; a root API key (`sk_`) is exempt. Requires the `profiles:c` scope.
|
|
2819
2819
|
*
|
|
2820
2820
|
* @param {Vectros.CreateAccessProfileRequest} request
|
|
2821
2821
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -3233,7 +3233,7 @@ var AuthClient = class {
|
|
|
3233
3233
|
);
|
|
3234
3234
|
}
|
|
3235
3235
|
/**
|
|
3236
|
-
* Updates an access profile. This is a partial update: any field you omit (or send as null) keeps its existing value. A profile must reference either inline `scopes` or a `roleId`, never both — so setting `scopes` clears any `roleId`, and setting `roleId` clears any inline `scopes`. The `contextId` and `principalId` are immutable. Status changes (for example active to suspended) take effect within about five minutes. If you use a scoped credential, the profile's effective scopes may not exceed your own
|
|
3236
|
+
* Updates an access profile. This is a partial update: any field you omit (or send as null) keeps its existing value. A profile must reference either inline `scopes` or a `roleId`, never both — so setting `scopes` clears any `roleId`, and setting `roleId` clears any inline `scopes`. The `contextId` and `principalId` are immutable. Status changes (for example active to suspended) take effect within about five minutes. If you use a scoped credential, the profile's effective scopes may not exceed your own, and its `identityOverrides` are bounded twice: you may only set a value your own identity holds, and you may only change or clear a value the profile already holds if that value is yours as well. Repointing or clearing another principal's established identity therefore returns 403. A root API key (`sk_`) is exempt. Requires the `profiles:u` scope.
|
|
3237
3237
|
*
|
|
3238
3238
|
* @param {Vectros.UpdateAccessProfileRequest} request
|
|
3239
3239
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -3306,7 +3306,7 @@ var AuthClient = class {
|
|
|
3306
3306
|
);
|
|
3307
3307
|
}
|
|
3308
3308
|
/**
|
|
3309
|
-
* Deletes an access profile. Within about five minutes (the access-profile cache lifetime), token minting for this principal in this context will be denied. Requires the `profiles:d` scope.
|
|
3309
|
+
* Deletes an access profile. Within about five minutes (the access-profile cache lifetime), token minting for this principal in this context will be denied. If you use a scoped credential and the profile carries `identityOverrides`, you may only delete it when you hold those values yourself — deleting a profile removes its identity, so the same bound applies as when clearing it. A profile with no `identityOverrides` is unaffected, and a root API key (`sk_`) is exempt. Requires the `profiles:d` scope.
|
|
3310
3310
|
*
|
|
3311
3311
|
* @param {Vectros.DeleteAccessProfileRequest} request
|
|
3312
3312
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4160,7 +4160,7 @@ var AuthClient = class {
|
|
|
4160
4160
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/auth/token");
|
|
4161
4161
|
}
|
|
4162
4162
|
/**
|
|
4163
|
-
* Invite a new member to one of your app contexts by email. Creates a pending user with a pre-resolved access profile (their permissions on accept) and signs an invitation token. This call is idempotent on the combination of context and email: re-inviting the same email in the same context rotates the token and resends the invitation rather than creating a duplicate. Returns HTTP 201 on a new invite or a successful resend. Returns 409 if that email already belongs to an active or suspended member of the app context, or already has an identity elsewhere in your account (an email can currently belong to only one tenant per account, i.e. your test and live environments cannot share an email). When `sendEmail` is false, the response includes the raw token and a ready-to-use accept link so you can deliver the invitation through your own email provider. Requires the `
|
|
4163
|
+
* Invite a new member to one of your app contexts by email. Creates a pending user with a pre-resolved access profile (their permissions on accept) and signs an invitation token. This call is idempotent on the combination of context and email: re-inviting the same email in the same context rotates the token and resends the invitation rather than creating a duplicate — this requires the `users:r` and `users:u` scopes in addition to `users:c`, because resending rotates a credential on an existing invitation and invalidates any link already sent. Without them the collision returns 409 instead, with no invitation details and no change to the outstanding invitation. Returns HTTP 201 on a new invite or a successful resend. Returns 409 if that email already belongs to an active or suspended member of the app context, or already has an identity elsewhere in your account (an email can currently belong to only one tenant per account, i.e. your test and live environments cannot share an email). When `sendEmail` is false, the response includes the raw token and a ready-to-use accept link so you can deliver the invitation through your own email provider. Requires the `users:c` scope.
|
|
4164
4164
|
*
|
|
4165
4165
|
* @param {Vectros.CreateInviteRequest} request
|
|
4166
4166
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4231,7 +4231,7 @@ var AuthClient = class {
|
|
|
4231
4231
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/users/invite");
|
|
4232
4232
|
}
|
|
4233
4233
|
/**
|
|
4234
|
-
* Resend an outstanding invitation, identified by its email and app context. Rotates the invitation token and extends its expiry, then (when `sendEmail` is true) re-delivers the email. Rotating the token invalidates any previously issued link for this invitation, so only the newest link works. The invitee's pending permissions are left unchanged.
|
|
4234
|
+
* Resend an outstanding invitation, identified by its email and app context. Rotates the invitation token and extends its expiry, then (when `sendEmail` is true) re-delivers the email. Rotating the token invalidates any previously issued link for this invitation, so only the newest link works. The invitee's pending permissions are left unchanged. Because this rotates a credential on an existing invitation, it requires the `users:c`, `users:r` and `users:u` scopes.
|
|
4235
4235
|
*
|
|
4236
4236
|
* @param {Vectros.CreateInviteRequest} request
|
|
4237
4237
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4565,7 +4565,7 @@ var DocumentsClient = class {
|
|
|
4565
4565
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
4566
4566
|
* scope: "group:eng-team",
|
|
4567
4567
|
* folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4568
|
-
* startFrom: "
|
|
4568
|
+
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl"
|
|
4569
4569
|
* })
|
|
4570
4570
|
*/
|
|
4571
4571
|
listDocuments(request = {}, requestOptions) {
|
|
@@ -4875,6 +4875,7 @@ var DocumentsClient = class {
|
|
|
4875
4875
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4876
4876
|
*
|
|
4877
4877
|
* @throws {@link Vectros.BadRequestError}
|
|
4878
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
4878
4879
|
* @throws {@link Vectros.NotFoundError}
|
|
4879
4880
|
* @throws {@link Vectros.ConflictError}
|
|
4880
4881
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
@@ -4922,6 +4923,8 @@ var DocumentsClient = class {
|
|
|
4922
4923
|
switch (_response.error.statusCode) {
|
|
4923
4924
|
case 400:
|
|
4924
4925
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4926
|
+
case 403:
|
|
4927
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4925
4928
|
case 404:
|
|
4926
4929
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4927
4930
|
case 409:
|
|
@@ -4952,7 +4955,7 @@ var DocumentsClient = class {
|
|
|
4952
4955
|
* field: "po_number",
|
|
4953
4956
|
* value: "PO-1001",
|
|
4954
4957
|
* prefix: "PO-2024",
|
|
4955
|
-
* startFrom: "
|
|
4958
|
+
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl",
|
|
4956
4959
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
4957
4960
|
* scope: "org:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
|
4958
4961
|
* })
|
|
@@ -5328,7 +5331,7 @@ var FoldersClient = class {
|
|
|
5328
5331
|
* parentFolderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
5329
5332
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
5330
5333
|
* scope: "group:eng-team",
|
|
5331
|
-
* startFrom: "
|
|
5334
|
+
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl"
|
|
5332
5335
|
* })
|
|
5333
5336
|
*/
|
|
5334
5337
|
listFolders(request = {}, requestOptions) {
|
|
@@ -5505,6 +5508,7 @@ var FoldersClient = class {
|
|
|
5505
5508
|
* @param {Vectros.UpdateFolderRequest} request
|
|
5506
5509
|
* @param {FoldersClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
5507
5510
|
*
|
|
5511
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
5508
5512
|
* @throws {@link Vectros.NotFoundError}
|
|
5509
5513
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5510
5514
|
*
|
|
@@ -5549,6 +5553,8 @@ var FoldersClient = class {
|
|
|
5549
5553
|
}
|
|
5550
5554
|
if (_response.error.reason === "status-code") {
|
|
5551
5555
|
switch (_response.error.statusCode) {
|
|
5556
|
+
case 403:
|
|
5557
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5552
5558
|
case 404:
|
|
5553
5559
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5554
5560
|
case 429:
|
|
@@ -5631,6 +5637,7 @@ var FoldersClient = class {
|
|
|
5631
5637
|
* @param {FoldersClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
5632
5638
|
*
|
|
5633
5639
|
* @throws {@link Vectros.BadRequestError}
|
|
5640
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
5634
5641
|
* @throws {@link Vectros.NotFoundError}
|
|
5635
5642
|
* @throws {@link Vectros.ConflictError}
|
|
5636
5643
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
@@ -5678,6 +5685,8 @@ var FoldersClient = class {
|
|
|
5678
5685
|
switch (_response.error.statusCode) {
|
|
5679
5686
|
case 400:
|
|
5680
5687
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
5688
|
+
case 403:
|
|
5689
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5681
5690
|
case 404:
|
|
5682
5691
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5683
5692
|
case 409:
|
|
@@ -5765,7 +5774,8 @@ var IdentityClient = class {
|
|
|
5765
5774
|
*
|
|
5766
5775
|
* @example
|
|
5767
5776
|
* await client.identity.listEntities({
|
|
5768
|
-
* namespace: "team"
|
|
5777
|
+
* namespace: "team",
|
|
5778
|
+
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl"
|
|
5769
5779
|
* })
|
|
5770
5780
|
*/
|
|
5771
5781
|
listEntities(request, requestOptions) {
|
|
@@ -6529,7 +6539,7 @@ var IdentityClient = class {
|
|
|
6529
6539
|
* @example
|
|
6530
6540
|
* await client.identity.listUsers({
|
|
6531
6541
|
* externalId: "usr_12345",
|
|
6532
|
-
* startFrom: "
|
|
6542
|
+
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl",
|
|
6533
6543
|
* type: "person_v1",
|
|
6534
6544
|
* field: "team",
|
|
6535
6545
|
* value: "engineering",
|
|
@@ -6710,11 +6720,12 @@ var IdentityClient = class {
|
|
|
6710
6720
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/users/{id}");
|
|
6711
6721
|
}
|
|
6712
6722
|
/**
|
|
6713
|
-
* Updates mutable fields on an existing user (such as email, status, payload, or schema binding). The `type` field is immutable after creation. This endpoint also activates an invited user: a PUT that moves a PENDING user to ACTIVE and carries `inviteToken`, `externalSubject`, and `emailVerifiedAttestation=true` completes the invitation. Requires the `users:u` scope.
|
|
6723
|
+
* Updates mutable fields on an existing user (such as email, status, payload, or schema binding). The `type` field is immutable after creation, and `email` cannot be changed while an invitation to that user is still outstanding — revoke the invitation, or invite the new address instead. This endpoint also activates an invited user: a PUT that moves a PENDING user to ACTIVE and carries `inviteToken`, `externalSubject`, and `emailVerifiedAttestation=true` completes the invitation. Requires the `users:u` scope.
|
|
6714
6724
|
*
|
|
6715
6725
|
* @param {Vectros.UpdateUserRequest} request
|
|
6716
6726
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
6717
6727
|
*
|
|
6728
|
+
* @throws {@link Vectros.BadRequestError}
|
|
6718
6729
|
* @throws {@link Vectros.NotFoundError}
|
|
6719
6730
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
6720
6731
|
*
|
|
@@ -6759,6 +6770,8 @@ var IdentityClient = class {
|
|
|
6759
6770
|
}
|
|
6760
6771
|
if (_response.error.reason === "status-code") {
|
|
6761
6772
|
switch (_response.error.statusCode) {
|
|
6773
|
+
case 400:
|
|
6774
|
+
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
6762
6775
|
case 404:
|
|
6763
6776
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
6764
6777
|
case 429:
|
|
@@ -7406,7 +7419,7 @@ var RecordsClient = class {
|
|
|
7406
7419
|
* folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
7407
7420
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
7408
7421
|
* scope: "group:eng-team",
|
|
7409
|
-
* startFrom: "
|
|
7422
|
+
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl"
|
|
7410
7423
|
* })
|
|
7411
7424
|
*/
|
|
7412
7425
|
listRecords(request = {}, requestOptions) {
|
|
@@ -7594,6 +7607,7 @@ var RecordsClient = class {
|
|
|
7594
7607
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
7595
7608
|
*
|
|
7596
7609
|
* @throws {@link Vectros.BadRequestError}
|
|
7610
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
7597
7611
|
* @throws {@link Vectros.NotFoundError}
|
|
7598
7612
|
* @throws {@link Vectros.ConflictError}
|
|
7599
7613
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
@@ -7642,6 +7656,8 @@ var RecordsClient = class {
|
|
|
7642
7656
|
switch (_response.error.statusCode) {
|
|
7643
7657
|
case 400:
|
|
7644
7658
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7659
|
+
case 403:
|
|
7660
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7645
7661
|
case 404:
|
|
7646
7662
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7647
7663
|
case 409:
|
|
@@ -7723,6 +7739,7 @@ var RecordsClient = class {
|
|
|
7723
7739
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
7724
7740
|
*
|
|
7725
7741
|
* @throws {@link Vectros.BadRequestError}
|
|
7742
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
7726
7743
|
* @throws {@link Vectros.NotFoundError}
|
|
7727
7744
|
* @throws {@link Vectros.ConflictError}
|
|
7728
7745
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
@@ -7773,6 +7790,8 @@ var RecordsClient = class {
|
|
|
7773
7790
|
switch (_response.error.statusCode) {
|
|
7774
7791
|
case 400:
|
|
7775
7792
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7793
|
+
case 403:
|
|
7794
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7776
7795
|
case 404:
|
|
7777
7796
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7778
7797
|
case 409:
|
|
@@ -7790,7 +7809,15 @@ var RecordsClient = class {
|
|
|
7790
7809
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "PATCH", "/v1/records/{id}");
|
|
7791
7810
|
}
|
|
7792
7811
|
/**
|
|
7793
|
-
* Finds records by the value of a lookup field declared on the type's schema. Provide exactly one lookup mode: `value` (exact match), `from`+`to` (inclusive range, ascending by value), or `prefix` (string fields only, ascending). Range and prefix lookups are not supported on a sensitive field, because its value is stored as a blind index and has no sortable order. An exact-`value` lookup on a sensitive field is also rejected on this GET endpoint — the value must not appear in the URL — so use the `POST /v1/records/lookup` body variant for sensitive fields.
|
|
7812
|
+
* Finds records by the value of a lookup field declared on the type's schema. Provide exactly one lookup mode: `value` (exact match), `from`+`to` (inclusive range, ascending by value), or `prefix` (string fields only, ascending). Range and prefix lookups are not supported on a sensitive field, because its value is stored as a blind index and has no sortable order. An exact-`value` lookup on a sensitive field is also rejected on this GET endpoint — the value must not appear in the URL — so use the `POST /v1/records/lookup` body variant for sensitive fields.
|
|
7813
|
+
*
|
|
7814
|
+
* A `value` lookup can additionally be narrowed to a window of the lookup field's **sort key** using `sortFrom` and/or `sortTo` (inclusive) — for example, one session's records created since a timestamp. The sort key is whatever the schema declares as that lookup's `sortBy` (`createdAt` by default, `lastUpdated`, or another field), and bounds are given in that field's own units — epoch milliseconds for the two timestamp options. **Records that have no value for the sorted field are never included in a bounded window** — they are ordered ahead of every record that does have one, and a `sortFrom`/`sortTo` window only ever selects from records carrying a value. The sorted field does not have to be `required`.
|
|
7815
|
+
*
|
|
7816
|
+
* Narrowing is available on any lookup field your schema declares for fast equality lookup, and on `externalId` — which is always ordered by creation time, so its bounds are epoch milliseconds whatever the schema says. It is rejected (`400`) for a field declared with `rangeEnabled`, for a field declared beyond the schema's fast-lookup budget, for a lookup whose `sortBy` names a sensitive field (a sensitive value is stored as a blind index and has no order), and for a window whose start is after its end. Ownership fields are not lookup fields on this endpoint at all — see the `field` parameter.
|
|
7817
|
+
*
|
|
7818
|
+
* While paging a narrowed lookup, keep every other parameter identical. The cursor is valid only for the exact query that returned it — changing `order`, `sortFrom`, `sortTo`, or dropping them altogether, is rejected rather than silently resumed at a position that means something different in the new query.
|
|
7819
|
+
*
|
|
7820
|
+
* Results are paginated: set `limit` for the page size and pass the returned `nextCursor` back as `startFrom` for the next page. **Keep paging until `nextCursor` is null** — a page can come back empty or shorter than `limit` while more results remain, so an empty page is not the end of the results. Requires the `records:r:<type>` scope.
|
|
7794
7821
|
*
|
|
7795
7822
|
* @param {Vectros.LookupRecordsRequest} request
|
|
7796
7823
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7802,22 +7829,40 @@ var RecordsClient = class {
|
|
|
7802
7829
|
* type: "intake_form",
|
|
7803
7830
|
* field: "email",
|
|
7804
7831
|
* value: "jane@example.com",
|
|
7832
|
+
* values: ["open"],
|
|
7805
7833
|
* prefix: "jane",
|
|
7806
|
-
* startFrom: "
|
|
7834
|
+
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl"
|
|
7807
7835
|
* })
|
|
7808
7836
|
*/
|
|
7809
7837
|
lookupRecords(request, requestOptions) {
|
|
7810
7838
|
return HttpResponsePromise.fromPromise(this.__lookupRecords(request, requestOptions));
|
|
7811
7839
|
}
|
|
7812
7840
|
async __lookupRecords(request, requestOptions) {
|
|
7813
|
-
const {
|
|
7841
|
+
const {
|
|
7842
|
+
type: type_,
|
|
7843
|
+
field,
|
|
7844
|
+
value,
|
|
7845
|
+
values,
|
|
7846
|
+
from: from_,
|
|
7847
|
+
to,
|
|
7848
|
+
prefix,
|
|
7849
|
+
sortFrom,
|
|
7850
|
+
sortTo,
|
|
7851
|
+
startFrom,
|
|
7852
|
+
limit,
|
|
7853
|
+
includePayload,
|
|
7854
|
+
order
|
|
7855
|
+
} = request;
|
|
7814
7856
|
const _queryParams = {
|
|
7815
7857
|
type: type_,
|
|
7816
7858
|
field,
|
|
7817
7859
|
value,
|
|
7860
|
+
values,
|
|
7818
7861
|
from: from_,
|
|
7819
7862
|
to,
|
|
7820
7863
|
prefix,
|
|
7864
|
+
sortFrom,
|
|
7865
|
+
sortTo,
|
|
7821
7866
|
startFrom,
|
|
7822
7867
|
limit,
|
|
7823
7868
|
includePayload,
|
|
@@ -7861,7 +7906,7 @@ var RecordsClient = class {
|
|
|
7861
7906
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/records/lookup");
|
|
7862
7907
|
}
|
|
7863
7908
|
/**
|
|
7864
|
-
* Body-based equivalent of `GET /v1/records/lookup`. Use this when looking up by a sensitive field: the value travels in the request body (and is blind-indexed server-side) instead of in the URL query string, so it never lands in access, CDN, or proxy logs. The GET variant rejects an exact-value lookup on a sensitive field and directs you here. Non-sensitive exact-value, range (`from`+`to`), and prefix lookups also work here. Returns the same `{data, nextCursor}` envelope and uses the same pagination as the GET variant. Requires the `records:r:<type>` scope.
|
|
7909
|
+
* Body-based equivalent of `GET /v1/records/lookup`. Use this when looking up by a sensitive field: the value travels in the request body (and is blind-indexed server-side) instead of in the URL query string, so it never lands in access, CDN, or proxy logs. The GET variant rejects an exact-value lookup on a sensitive field and directs you here. Non-sensitive exact-value, range (`from`+`to`), and prefix lookups also work here, as does narrowing a `value` lookup by the field's sort key with `sortFrom`/`sortTo` — see the GET variant for what the sort key is and when narrowing by it is available. Returns the same `{data, nextCursor}` envelope and uses the same pagination as the GET variant; keep paging until `nextCursor` is null rather than stopping on an empty page. Requires the `records:r:<type>` scope.
|
|
7865
7910
|
*
|
|
7866
7911
|
* @param {Vectros.RecordLookupRequest} request
|
|
7867
7912
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -8056,7 +8101,7 @@ var SchemasClient = class {
|
|
|
8056
8101
|
* scope: "org:6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
8057
8102
|
* surface: "document",
|
|
8058
8103
|
* recordType: "intake_form",
|
|
8059
|
-
* startFrom: "
|
|
8104
|
+
* startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl"
|
|
8060
8105
|
* })
|
|
8061
8106
|
*/
|
|
8062
8107
|
listSchemas(request = {}, requestOptions) {
|
|
@@ -8260,6 +8305,7 @@ var SchemasClient = class {
|
|
|
8260
8305
|
* @param {SchemasClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
8261
8306
|
*
|
|
8262
8307
|
* @throws {@link Vectros.BadRequestError}
|
|
8308
|
+
* @throws {@link Vectros.ForbiddenError}
|
|
8263
8309
|
* @throws {@link Vectros.NotFoundError}
|
|
8264
8310
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8265
8311
|
*
|
|
@@ -8308,6 +8354,8 @@ var SchemasClient = class {
|
|
|
8308
8354
|
switch (_response.error.statusCode) {
|
|
8309
8355
|
case 400:
|
|
8310
8356
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
8357
|
+
case 403:
|
|
8358
|
+
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
8311
8359
|
case 404:
|
|
8312
8360
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8313
8361
|
case 429:
|
|
@@ -8625,8 +8673,8 @@ function warnOnSuboptimalKey(key, log) {
|
|
|
8625
8673
|
|
|
8626
8674
|
// src/build-info.ts
|
|
8627
8675
|
var BUILD_INFO = {
|
|
8628
|
-
mcpServer: true ? "0.
|
|
8629
|
-
sdk: true ? "0.
|
|
8676
|
+
mcpServer: true ? "0.13.0" : "dev",
|
|
8677
|
+
sdk: true ? "0.38.0" : "dev"
|
|
8630
8678
|
};
|
|
8631
8679
|
|
|
8632
8680
|
// src/log.ts
|
|
@@ -8666,7 +8714,9 @@ async function resolveIdentity({
|
|
|
8666
8714
|
const derived = {
|
|
8667
8715
|
status: "ok",
|
|
8668
8716
|
environment: deriveEnvironment(environment),
|
|
8669
|
-
principalType: derivePrincipalType(apiKey)
|
|
8717
|
+
principalType: derivePrincipalType(apiKey),
|
|
8718
|
+
mcpServerVersion: BUILD_INFO.mcpServer,
|
|
8719
|
+
sdkVersion: BUILD_INFO.sdk
|
|
8670
8720
|
};
|
|
8671
8721
|
if (!apiKey || !environment) {
|
|
8672
8722
|
log.debug({ component: "identity", mode: "derived-only" }, "identity derived (no fetch)");
|
|
@@ -8694,7 +8744,13 @@ async function resolveIdentity({
|
|
|
8694
8744
|
);
|
|
8695
8745
|
}
|
|
8696
8746
|
}
|
|
8697
|
-
const merged = {
|
|
8747
|
+
const merged = {
|
|
8748
|
+
...derived,
|
|
8749
|
+
...extended,
|
|
8750
|
+
status: "ok",
|
|
8751
|
+
mcpServerVersion: derived.mcpServerVersion,
|
|
8752
|
+
sdkVersion: derived.sdkVersion
|
|
8753
|
+
};
|
|
8698
8754
|
log.debug(
|
|
8699
8755
|
{
|
|
8700
8756
|
component: "identity",
|
|
@@ -9016,14 +9072,23 @@ import { z as z2 } from "zod";
|
|
|
9016
9072
|
function pageItems(page) {
|
|
9017
9073
|
return page.data ?? [];
|
|
9018
9074
|
}
|
|
9075
|
+
var MAX_PAGES = 100;
|
|
9076
|
+
var MAX_ROWS = 1e6;
|
|
9019
9077
|
async function drainPages(fetchPage) {
|
|
9020
9078
|
const all = [];
|
|
9021
9079
|
let startFrom;
|
|
9080
|
+
let pages = 0;
|
|
9022
9081
|
for (; ; ) {
|
|
9023
9082
|
const page = await fetchPage(startFrom);
|
|
9024
|
-
|
|
9083
|
+
pages++;
|
|
9084
|
+
if (page.data?.length) for (const item of page.data) all.push(item);
|
|
9025
9085
|
const next = page.nextCursor;
|
|
9026
9086
|
if (!next) break;
|
|
9087
|
+
if (pages > MAX_PAGES || all.length > MAX_ROWS) {
|
|
9088
|
+
throw new Error(
|
|
9089
|
+
`drainPages: exceeded the runaway guard (${all.length} rows over ${pages} pages, limits ${MAX_ROWS}/${MAX_PAGES}) without the cursor going null. Refusing to return a partial result \u2014 the backend may be returning empty pages with a live cursor.`
|
|
9090
|
+
);
|
|
9091
|
+
}
|
|
9027
9092
|
startFrom = next;
|
|
9028
9093
|
}
|
|
9029
9094
|
return all;
|
|
@@ -9034,14 +9099,23 @@ var MCP_DEFAULT_LIMIT2 = 3;
|
|
|
9034
9099
|
var MCP_MAX_LIMIT2 = 100;
|
|
9035
9100
|
var inputSchema2 = {
|
|
9036
9101
|
type: z2.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
|
|
9037
|
-
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
9102
|
+
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | values | from+to | prefix.
|
|
9038
9103
|
field: z2.string().optional().describe(
|
|
9039
|
-
'Lookup mode: name of the lookup-indexed field to query by. `externalId` is always queryable \u2014 e.g. `{type:"control", field:"externalId", value:"ctrl-scoped-key"}` finds the record you created under that externalId. (record_get / record_update also accept externalId directly.)'
|
|
9104
|
+
'Lookup mode: name of the lookup-indexed field to query by. `externalId` is always queryable \u2014 e.g. `{type:"control", field:"externalId", value:"ctrl-scoped-key"}` finds the record you created under that externalId. (record_get / record_update also accept externalId directly.) For a SCHEMA-declared composite lookup \u2014 check list_schemas\' lookupFields for a fieldNames entry, not any two fields you pick \u2014 give those field names comma-joined in the declared order (e.g. "status,area"). Use `values` to match on several of those fields at once, or `value` to match on just the FIRST field named (equivalent to a one-element `values`); range/prefix are not valid on a composite field.'
|
|
9105
|
+
),
|
|
9106
|
+
value: z2.string().optional().describe(
|
|
9107
|
+
"Lookup mode (equality): exact-match value for `field`. Also valid when `field` names a SCHEMA-declared composite lookup \u2014 matches just the first named field, grouping by the rest (equivalent to `values` with one entry)."
|
|
9108
|
+
),
|
|
9109
|
+
values: z2.array(z2.string()).min(1).max(3).optional().describe(
|
|
9110
|
+
'Lookup mode (composite equality): only valid against a lookup the SCHEMA declares over several fields together (a fieldNames entry in list_schemas \u2014 you cannot combine two ordinary single-field lookups this way). One value per field, in the order the schema declares them (e.g. field="status,area", values=["open","billing"]). Mutually exclusive with `value`; a single-element array is exactly `value` written uniformly. Supplying FEWER values than the lookup declares is allowed \u2014 as long as they\'re a leading run of its fields \u2014 and returns records grouped by the fields left unspecified; `order` then sorts within each group, not across them. Record lookups only (not documents/users/entities).'
|
|
9040
9111
|
),
|
|
9041
|
-
value: z2.string().optional().describe("Lookup mode (equality): exact-match value for `field`."),
|
|
9042
9112
|
from: z2.string().optional().describe("Lookup mode (range): inclusive lower bound; requires `to`. Non-sensitive fields only."),
|
|
9043
9113
|
to: z2.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
|
|
9044
9114
|
prefix: z2.string().optional().describe("Lookup mode (prefix): match values starting with this. String, non-sensitive fields only."),
|
|
9115
|
+
sortFrom: z2.string().optional().describe(
|
|
9116
|
+
"Lookup mode (equality/composite only): inclusive lower bound on the lookup field's sort key, narrowing a `value`/`values` match to a window instead of paging the whole match. Give the bound in the sort field's own units (epoch milliseconds for `createdAt`/`lastUpdated`). With `values`, requires every value be supplied (no grouping) for one continuous ordering. Not valid with `from`/`to`/`prefix`."
|
|
9117
|
+
),
|
|
9118
|
+
sortTo: z2.string().optional().describe("Lookup mode (equality/composite only): inclusive upper bound on the sort key; pairs with `sortFrom`."),
|
|
9045
9119
|
order: z2.enum(["asc", "desc"]).optional().describe(
|
|
9046
9120
|
"Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc` \u2014 use `desc` with a range/prefix lookup to get the most-recent / highest values first (e.g. latest-N). Ignored in list mode."
|
|
9047
9121
|
),
|
|
@@ -9058,54 +9132,106 @@ var inputSchema2 = {
|
|
|
9058
9132
|
var recordQuery = ({ client, log }) => ({
|
|
9059
9133
|
name: "record_query",
|
|
9060
9134
|
title: "Record query (list or lookup)",
|
|
9061
|
-
description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too
|
|
9135
|
+
description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `values` (exact, composite \u2014 one value per field; a single-element array is the same as `value`), `from`+`to` (range), or `prefix`. `values` with more than one entry, or a comma-joined `field`, ONLY works for a lookup the schema itself declared over several fields together \u2014 check list_schemas' lookupFields for a fieldNames entry, then pass those field names comma-joined as `field`; `from`/`to`/`prefix` are not valid there. Works on sensitive fields too. Optionally narrow an exact match to a window with `sortFrom`/`sortTo`.\n \u2022 List by type: omit `field`; optionally filter by `userId` or `scope` (`namespace:value`).\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` records (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. A composite lookup given fewer than its full field count returns records grouped by the unspecified fields, ordered within each group. Mode is auto-detected from the arguments present.",
|
|
9062
9136
|
inputSchema: inputSchema2,
|
|
9063
9137
|
handler: async (args) => {
|
|
9064
9138
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
|
|
9065
9139
|
const type = args.type;
|
|
9066
9140
|
const field = args.field;
|
|
9067
9141
|
const value = args.value;
|
|
9142
|
+
const values = args.values;
|
|
9068
9143
|
const from = args.from;
|
|
9069
9144
|
const to = args.to;
|
|
9070
9145
|
const prefix = args.prefix;
|
|
9146
|
+
const sortFrom = args.sortFrom;
|
|
9147
|
+
const sortTo = args.sortTo;
|
|
9071
9148
|
const startFrom = args.startFrom;
|
|
9149
|
+
if (!field && (value !== void 0 || values !== void 0 || from !== void 0 || to !== void 0 || prefix !== void 0 || sortFrom !== void 0 || sortTo !== void 0)) {
|
|
9150
|
+
return toolError(
|
|
9151
|
+
"record_query",
|
|
9152
|
+
new Error(
|
|
9153
|
+
"'value'/'values'/'from'/'to'/'prefix'/'sortFrom'/'sortTo' require 'field' \u2014 without it, list mode runs and silently ignores them. Pass 'field' to run a lookup, or drop these to list by type."
|
|
9154
|
+
)
|
|
9155
|
+
);
|
|
9156
|
+
}
|
|
9072
9157
|
try {
|
|
9073
9158
|
let page;
|
|
9074
9159
|
if (field) {
|
|
9160
|
+
const fieldNames = field.split(",").map((f) => f.trim()).filter(Boolean);
|
|
9161
|
+
const normalizedField = fieldNames.join(",");
|
|
9162
|
+
const isCompositeField = fieldNames.length > 1;
|
|
9075
9163
|
const hasEquality = value !== void 0;
|
|
9164
|
+
const hasComposite = values !== void 0;
|
|
9076
9165
|
const hasRange = from !== void 0 || to !== void 0;
|
|
9077
9166
|
const hasPrefix = prefix !== void 0;
|
|
9078
|
-
const modes = Number(hasEquality) + Number(hasRange) + Number(hasPrefix);
|
|
9167
|
+
const modes = Number(hasEquality) + Number(hasComposite) + Number(hasRange) + Number(hasPrefix);
|
|
9079
9168
|
if (modes === 0) {
|
|
9080
9169
|
return toolError(
|
|
9081
9170
|
"record_query",
|
|
9082
9171
|
new Error(
|
|
9083
|
-
`lookup on field '${field}' needs one of: 'value' (exact), 'from'+'to' (range), or 'prefix'.`
|
|
9172
|
+
`lookup on field '${field}' needs one of: 'value' (exact), 'values' (composite exact), 'from'+'to' (range), or 'prefix'.`
|
|
9084
9173
|
)
|
|
9085
9174
|
);
|
|
9086
9175
|
}
|
|
9087
9176
|
if (modes > 1) {
|
|
9088
9177
|
return toolError(
|
|
9089
9178
|
"record_query",
|
|
9090
|
-
new Error(
|
|
9179
|
+
new Error(
|
|
9180
|
+
"'value', 'values', 'from'/'to', and 'prefix' are mutually exclusive \u2014 provide exactly one."
|
|
9181
|
+
)
|
|
9091
9182
|
);
|
|
9092
9183
|
}
|
|
9093
9184
|
if (hasRange && !(from !== void 0 && to !== void 0)) {
|
|
9094
9185
|
return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
|
|
9095
9186
|
}
|
|
9187
|
+
if (isCompositeField && (hasRange || hasPrefix)) {
|
|
9188
|
+
return toolError(
|
|
9189
|
+
"record_query",
|
|
9190
|
+
new Error(
|
|
9191
|
+
`'field' names ${fieldNames.length} fields ('${normalizedField}') \u2014 a composite lookup is exact-match only ('value' or 'values'), not 'from'/'to'/'prefix'.`
|
|
9192
|
+
)
|
|
9193
|
+
);
|
|
9194
|
+
}
|
|
9195
|
+
if (hasComposite && values.length > fieldNames.length) {
|
|
9196
|
+
return toolError(
|
|
9197
|
+
"record_query",
|
|
9198
|
+
new Error(
|
|
9199
|
+
`'values' has ${values.length} entries but 'field' names only ${fieldNames.length} field(s) ('${normalizedField}') \u2014 supply at most one value per field, in order (fewer only as a leading run).`
|
|
9200
|
+
)
|
|
9201
|
+
);
|
|
9202
|
+
}
|
|
9203
|
+
if (sortFrom !== void 0 || sortTo !== void 0) {
|
|
9204
|
+
if (!(hasEquality || hasComposite)) {
|
|
9205
|
+
return toolError(
|
|
9206
|
+
"record_query",
|
|
9207
|
+
new Error("'sortFrom'/'sortTo' narrow a 'value' or 'values' match only \u2014 not 'from'/'to'/'prefix'.")
|
|
9208
|
+
);
|
|
9209
|
+
}
|
|
9210
|
+
if (hasComposite && values.length < fieldNames.length) {
|
|
9211
|
+
return toolError(
|
|
9212
|
+
"record_query",
|
|
9213
|
+
new Error(
|
|
9214
|
+
`'sortFrom'/'sortTo' with 'values' requires a value for every field named in 'field' (got ${values.length} of ${fieldNames.length}) \u2014 a partial/grouped match has no single continuous ordering to window.`
|
|
9215
|
+
)
|
|
9216
|
+
);
|
|
9217
|
+
}
|
|
9218
|
+
}
|
|
9096
9219
|
page = await client.records.lookupRecordsByBody({
|
|
9097
9220
|
type,
|
|
9098
|
-
field,
|
|
9221
|
+
field: normalizedField,
|
|
9099
9222
|
value,
|
|
9223
|
+
values,
|
|
9100
9224
|
from,
|
|
9101
9225
|
to,
|
|
9102
9226
|
prefix,
|
|
9227
|
+
sortFrom,
|
|
9228
|
+
sortTo,
|
|
9103
9229
|
order: args.order,
|
|
9104
9230
|
startFrom,
|
|
9105
9231
|
limit
|
|
9106
9232
|
});
|
|
9107
9233
|
log.debug(
|
|
9108
|
-
{ tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9234
|
+
{ tool: "record_query", mode: "lookup", type, field: normalizedField, returned: pageItems(page).length },
|
|
9109
9235
|
"record_query lookup ok"
|
|
9110
9236
|
);
|
|
9111
9237
|
} else {
|
|
@@ -10044,7 +10170,7 @@ var inputSchema18 = {
|
|
|
10044
10170
|
var currentIdentity = ({ log, apiKey, environment }) => ({
|
|
10045
10171
|
name: "current_identity",
|
|
10046
10172
|
title: "Current identity (tenant + principal scope)",
|
|
10047
|
-
description: "Describe the credential the MCP server is operating under. Returns tenantId, environment (staging|production), principalType (root_key|scoped_key|token), principalKeyId, principalLabel, and (for scoped credentials) allowedActions plus dataScope \u2014 the credential's reach as { userId, scopes[] }, where each scope is a `namespace:value` string (e.g. \"org:<uuid>\"). Use this when the user asks 'what can you do here?'
|
|
10173
|
+
description: "Describe the credential the MCP server is operating under. Returns tenantId, environment (staging|production), principalType (root_key|scoped_key|token), principalKeyId, principalLabel, and (for scoped credentials) allowedActions plus dataScope \u2014 the credential's reach as { userId, scopes[] }, where each scope is a `namespace:value` string (e.g. \"org:<uuid>\"). Also reports mcpServerVersion (this server's own package version) and sdkVersion (the bundled @vectros-ai/sdk version) so a caller can tell what build it's talking to. Use this when the user asks 'what can you do here?', 'what tenant am I in?', or 'what version is this?'. Calls GET /v1/ping under the hood.",
|
|
10048
10174
|
inputSchema: inputSchema18,
|
|
10049
10175
|
handler: async () => {
|
|
10050
10176
|
try {
|
|
@@ -10522,7 +10648,7 @@ var identityResource = ({ log, apiKey, environment }) => ({
|
|
|
10522
10648
|
name: "identity",
|
|
10523
10649
|
uri: "vectros://identity",
|
|
10524
10650
|
title: "Current identity (tenant + principal scope)",
|
|
10525
|
-
description: "The tenant + principal scope the MCP server is authenticated as. Returns the same payload as the `current_identity` tool: status, tenantId, environment, principalType, principalKeyId, principalLabel,
|
|
10651
|
+
description: "The tenant + principal scope the MCP server is authenticated as. Returns the same payload as the `current_identity` tool: status, tenantId, environment, principalType, principalKeyId, principalLabel, (for scoped credentials) allowedActions + dataScope ({ userId, scopes[] }), plus mcpServerVersion and sdkVersion (this build's own version and the bundled @vectros-ai/sdk version). The /v1/ping backing call ships the extended shape today; the version fields are always client-derived.",
|
|
10526
10652
|
mimeType: "application/json",
|
|
10527
10653
|
read: async () => {
|
|
10528
10654
|
const identity = await resolveIdentity({ log, apiKey, environment });
|