@vectros-ai/cli 0.24.0 → 0.26.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/AUTHORING.md +37 -20
- package/CHANGELOG.md +107 -0
- package/README.md +69 -25
- package/dist/cli.js +563 -278
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +563 -278
- package/dist/cli.mjs.map +1 -1
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -1614,7 +1614,7 @@ var require_proper_lockfile = __commonJS({
|
|
|
1614
1614
|
// src/cli-bootstrap.ts
|
|
1615
1615
|
var readline2 = __toESM(require("readline/promises"));
|
|
1616
1616
|
|
|
1617
|
-
// node_modules/@vectros-ai/sdk/dist/index.mjs
|
|
1617
|
+
// ../../node_modules/@vectros-ai/sdk/dist/index.mjs
|
|
1618
1618
|
var __defProp2 = Object.defineProperty;
|
|
1619
1619
|
var __export = (target, all) => {
|
|
1620
1620
|
for (var name in all)
|
|
@@ -1629,9 +1629,11 @@ __export(api_exports, {
|
|
|
1629
1629
|
BatchWriteRequest: () => BatchWriteRequest,
|
|
1630
1630
|
BatchWriteResult: () => BatchWriteResult,
|
|
1631
1631
|
ChatMessage: () => ChatMessage,
|
|
1632
|
+
ChatRequest: () => ChatRequest,
|
|
1632
1633
|
ConflictError: () => ConflictError,
|
|
1633
1634
|
ContentDeltaEvent: () => ContentDeltaEvent,
|
|
1634
1635
|
ContentTooLargeError: () => ContentTooLargeError,
|
|
1636
|
+
DocumentAskRequest: () => DocumentAskRequest,
|
|
1635
1637
|
DocumentContextEvent: () => DocumentContextEvent,
|
|
1636
1638
|
DocumentLookupRequest: () => DocumentLookupRequest,
|
|
1637
1639
|
DocumentPatchRequest: () => DocumentPatchRequest,
|
|
@@ -1662,8 +1664,10 @@ __export(api_exports, {
|
|
|
1662
1664
|
NotImplementedError: () => NotImplementedError,
|
|
1663
1665
|
PaymentRequiredError: () => PaymentRequiredError,
|
|
1664
1666
|
PingResponse: () => PingResponse,
|
|
1667
|
+
RagRequest: () => RagRequest,
|
|
1665
1668
|
RagSearch: () => RagSearch,
|
|
1666
1669
|
ReadAccessLogResponse: () => ReadAccessLogResponse,
|
|
1670
|
+
ReasoningDeltaEvent: () => ReasoningDeltaEvent,
|
|
1667
1671
|
RecordLookupRequest: () => RecordLookupRequest,
|
|
1668
1672
|
RecordRequest: () => RecordRequest,
|
|
1669
1673
|
RecordResponse: () => RecordResponse,
|
|
@@ -2019,7 +2023,33 @@ var ListUsersRequestOrder = {
|
|
|
2019
2023
|
Asc: "asc",
|
|
2020
2024
|
Desc: "desc"
|
|
2021
2025
|
};
|
|
2026
|
+
var ChatRequest;
|
|
2027
|
+
((ChatRequest2) => {
|
|
2028
|
+
ChatRequest2.Reasoning = {
|
|
2029
|
+
Omit: "omit",
|
|
2030
|
+
Stream: "stream"
|
|
2031
|
+
};
|
|
2032
|
+
})(ChatRequest || (ChatRequest = {}));
|
|
2033
|
+
var DocumentAskRequest;
|
|
2034
|
+
((DocumentAskRequest2) => {
|
|
2035
|
+
DocumentAskRequest2.Reasoning = {
|
|
2036
|
+
Omit: "omit",
|
|
2037
|
+
Stream: "stream"
|
|
2038
|
+
};
|
|
2039
|
+
})(DocumentAskRequest || (DocumentAskRequest = {}));
|
|
2040
|
+
var RagRequest;
|
|
2041
|
+
((RagRequest2) => {
|
|
2042
|
+
RagRequest2.Reasoning = {
|
|
2043
|
+
Omit: "omit",
|
|
2044
|
+
Stream: "stream"
|
|
2045
|
+
};
|
|
2046
|
+
})(RagRequest || (RagRequest = {}));
|
|
2022
2047
|
var inference_exports = {};
|
|
2048
|
+
__export(inference_exports, {
|
|
2049
|
+
ChatRequest: () => ChatRequest,
|
|
2050
|
+
DocumentAskRequest: () => DocumentAskRequest,
|
|
2051
|
+
RagRequest: () => RagRequest
|
|
2052
|
+
});
|
|
2023
2053
|
var BatchWriteRequest;
|
|
2024
2054
|
((BatchWriteRequest2) => {
|
|
2025
2055
|
BatchWriteRequest2.Atomicity = {
|
|
@@ -2292,6 +2322,12 @@ var ReadAccessLogResponse;
|
|
|
2292
2322
|
Rag: "rag"
|
|
2293
2323
|
};
|
|
2294
2324
|
})(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
|
|
2325
|
+
var ReasoningDeltaEvent;
|
|
2326
|
+
((ReasoningDeltaEvent2) => {
|
|
2327
|
+
ReasoningDeltaEvent2.Event = {
|
|
2328
|
+
ReasoningDelta: "reasoning_delta"
|
|
2329
|
+
};
|
|
2330
|
+
})(ReasoningDeltaEvent || (ReasoningDeltaEvent = {}));
|
|
2295
2331
|
var RecordRequest;
|
|
2296
2332
|
((RecordRequest2) => {
|
|
2297
2333
|
RecordRequest2.Status = {
|
|
@@ -4044,7 +4080,7 @@ var AuthClient = class {
|
|
|
4044
4080
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/jwks");
|
|
4045
4081
|
}
|
|
4046
4082
|
/**
|
|
4047
|
-
* Returns a page of per-subject PHI read-access rows: who read which subject's PHI, when, against which record, and whether any sensitive value was actually revealed in plaintext. Metadata only — never the PHI itself.
|
|
4083
|
+
* Returns a page of per-subject PHI read-access rows: who read which subject's PHI, when, against which record, and whether any sensitive value was actually revealed in plaintext. Metadata only — never the PHI itself. Rows exist only where read-access logging is enabled. Provide at least one query axis: a subject (`subjectType` + `subjectId`) within a `contextId` for the primary accounting query; `resourceId` within a `contextId` for 'who read this record'; `callerKeyId` for 'what did this credential read' (account-wide forensic); or `contextId` alone to enumerate a whole context. `from`/`to` bound the time window. Results are scoped to your account, derived from your token — never from input. Requires the `access-log:r` scope.
|
|
4048
4084
|
*
|
|
4049
4085
|
* @param {Vectros.GetAccessLogRequest} request
|
|
4050
4086
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4186,7 +4222,7 @@ var AuthClient = class {
|
|
|
4186
4222
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/admin/keys/scoped");
|
|
4187
4223
|
}
|
|
4188
4224
|
/**
|
|
4189
|
-
* Creates a scoped API key (an `ssk_*` secret) that inherits its permissions from an existing access profile in your account. The call is idempotent on the combination of tenant, context, user, and key name: re-issuing the same request returns the existing key WITHOUT re-disclosing its raw secret. The raw key is returned ONLY in this response — store it securely, as it cannot be retrieved again. Requires the `keys:c` scope. If you use a scoped credential, `keys:c` alone is not sufficient: because the minted key is durably bound to the profile you name, the profile's effective scopes may not exceed your own, and you may only mint against a profile whose `identityOverrides` values your own identity holds. Minting a key bound to your OWN principal needs nothing further; minting one bound to a DIFFERENT principal additionally requires the `delegate-mint` capability (`granted_capabilities`) on your credential — without it the request is refused. A root API key (`sk_`) is exempt from all three bounds. The profile you name must be `active` AND the user you name must not be `SUSPENDED`: either is refused with `409`, and no root exemption applies to that. Both refusals stop ISSUANCE only. Suspending the profile additionally stops credentials already bound to it, within about five minutes
|
|
4225
|
+
* Creates a scoped API key (an `ssk_*` secret) that inherits its permissions from an existing access profile in your account. The call is idempotent on the combination of tenant, context, user, and key name: re-issuing the same request returns the existing key WITHOUT re-disclosing its raw secret. The raw key is returned ONLY in this response — store it securely, as it cannot be retrieved again. Requires the `keys:c` scope. If you use a scoped credential, `keys:c` alone is not sufficient: because the minted key is durably bound to the profile you name, the profile's effective scopes may not exceed your own, and you may only mint against a profile whose `identityOverrides` values your own identity holds. Minting a key bound to your OWN principal needs nothing further; minting one bound to a DIFFERENT principal additionally requires the `delegate-mint` capability (`granted_capabilities`) on your credential — without it the request is refused. A root API key (`sk_`) is exempt from all three bounds. The profile you name must be `active` AND the user you name must not be `SUSPENDED`: either is refused with `409`, and no root exemption applies to that. Both refusals stop ISSUANCE only. Suspending the profile or the user additionally stops credentials already bound to it, within about five minutes. A `PENDING` user is fine.
|
|
4190
4226
|
*
|
|
4191
4227
|
* @param {Vectros.CreateScopedKeyRequest} request
|
|
4192
4228
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4529,6 +4565,7 @@ var AuthClient = class {
|
|
|
4529
4565
|
* @throws {@link Vectros.BadRequestError}
|
|
4530
4566
|
* @throws {@link Vectros.ForbiddenError}
|
|
4531
4567
|
* @throws {@link Vectros.NotFoundError}
|
|
4568
|
+
* @throws {@link Vectros.ConflictError}
|
|
4532
4569
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4533
4570
|
*
|
|
4534
4571
|
* @example
|
|
@@ -4581,6 +4618,8 @@ var AuthClient = class {
|
|
|
4581
4618
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4582
4619
|
case 404:
|
|
4583
4620
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4621
|
+
case 409:
|
|
4622
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4584
4623
|
case 429:
|
|
4585
4624
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4586
4625
|
default:
|
|
@@ -4664,6 +4703,8 @@ var AuthClient = class {
|
|
|
4664
4703
|
* @throws {@link Vectros.BadRequestError}
|
|
4665
4704
|
* @throws {@link Vectros.PaymentRequiredError}
|
|
4666
4705
|
* @throws {@link Vectros.ForbiddenError}
|
|
4706
|
+
* @throws {@link Vectros.NotFoundError}
|
|
4707
|
+
* @throws {@link Vectros.ConflictError}
|
|
4667
4708
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4668
4709
|
*
|
|
4669
4710
|
* @example
|
|
@@ -4716,6 +4757,10 @@ var AuthClient = class {
|
|
|
4716
4757
|
throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
|
|
4717
4758
|
case 403:
|
|
4718
4759
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4760
|
+
case 404:
|
|
4761
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4762
|
+
case 409:
|
|
4763
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4719
4764
|
case 429:
|
|
4720
4765
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4721
4766
|
default:
|
|
@@ -4804,6 +4849,7 @@ var AuthClient = class {
|
|
|
4804
4849
|
* @throws {@link Vectros.BadRequestError}
|
|
4805
4850
|
* @throws {@link Vectros.ForbiddenError}
|
|
4806
4851
|
* @throws {@link Vectros.NotFoundError}
|
|
4852
|
+
* @throws {@link Vectros.ConflictError}
|
|
4807
4853
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4808
4854
|
*
|
|
4809
4855
|
* @example
|
|
@@ -4860,6 +4906,8 @@ var AuthClient = class {
|
|
|
4860
4906
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4861
4907
|
case 404:
|
|
4862
4908
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4909
|
+
case 409:
|
|
4910
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4863
4911
|
case 429:
|
|
4864
4912
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4865
4913
|
default:
|
|
@@ -4950,6 +4998,7 @@ var AuthClient = class {
|
|
|
4950
4998
|
* @throws {@link Vectros.BadRequestError}
|
|
4951
4999
|
* @throws {@link Vectros.ForbiddenError}
|
|
4952
5000
|
* @throws {@link Vectros.NotFoundError}
|
|
5001
|
+
* @throws {@link Vectros.ConflictError}
|
|
4953
5002
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4954
5003
|
*
|
|
4955
5004
|
* @example
|
|
@@ -5000,6 +5049,8 @@ var AuthClient = class {
|
|
|
5000
5049
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5001
5050
|
case 404:
|
|
5002
5051
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5052
|
+
case 409:
|
|
5053
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5003
5054
|
case 429:
|
|
5004
5055
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5005
5056
|
default:
|
|
@@ -5146,13 +5197,14 @@ var AuthClient = class {
|
|
|
5146
5197
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/app-contexts/{contextId}");
|
|
5147
5198
|
}
|
|
5148
5199
|
/**
|
|
5149
|
-
* Updates an app context. This is a partial update: any field you omit (or send as null) keeps its existing value. The `contextId` is immutable and is taken from the URL path, so any `contextId` in the request body is ignored. Patchable fields: `name`, `description`, `companyName`; the per-principal metering trio (`meteringAxis`, `principalBurstLimit`, `principalUsageCap` — only takes effect for
|
|
5200
|
+
* Updates an app context. This is a partial update: any field you omit (or send as null) keeps its existing value. The `contextId` is immutable and is taken from the URL path, so any `contextId` in the request body is ignored. Patchable fields: `name`, `description`, `companyName`; the per-principal metering trio (`meteringAxis`, `principalBurstLimit`, `principalUsageCap` — only takes effect for an account with the corresponding feature enabled); `readAccessLogDefault` (the PHI read-access-logging context default); and `identityProjectionClaims` (requires the platform provisioning capability on top of the ordinary scope below — see its own field description). Requires the `app-contexts:u` scope.
|
|
5150
5201
|
*
|
|
5151
5202
|
* @param {Vectros.UpdateAppContextRequest} request
|
|
5152
5203
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
5153
5204
|
*
|
|
5154
5205
|
* @throws {@link Vectros.ForbiddenError}
|
|
5155
5206
|
* @throws {@link Vectros.NotFoundError}
|
|
5207
|
+
* @throws {@link Vectros.ConflictError}
|
|
5156
5208
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5157
5209
|
*
|
|
5158
5210
|
* @example
|
|
@@ -5201,6 +5253,8 @@ var AuthClient = class {
|
|
|
5201
5253
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5202
5254
|
case 404:
|
|
5203
5255
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5256
|
+
case 409:
|
|
5257
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5204
5258
|
case 429:
|
|
5205
5259
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5206
5260
|
default:
|
|
@@ -5357,6 +5411,7 @@ var AuthClient = class {
|
|
|
5357
5411
|
*
|
|
5358
5412
|
* @throws {@link Vectros.ForbiddenError}
|
|
5359
5413
|
* @throws {@link Vectros.NotFoundError}
|
|
5414
|
+
* @throws {@link Vectros.ConflictError}
|
|
5360
5415
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5361
5416
|
*
|
|
5362
5417
|
* @example
|
|
@@ -5409,6 +5464,8 @@ var AuthClient = class {
|
|
|
5409
5464
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5410
5465
|
case 404:
|
|
5411
5466
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5467
|
+
case 409:
|
|
5468
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5412
5469
|
case 429:
|
|
5413
5470
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5414
5471
|
default:
|
|
@@ -5434,6 +5491,7 @@ var AuthClient = class {
|
|
|
5434
5491
|
*
|
|
5435
5492
|
* @throws {@link Vectros.ForbiddenError}
|
|
5436
5493
|
* @throws {@link Vectros.NotFoundError}
|
|
5494
|
+
* @throws {@link Vectros.ConflictError}
|
|
5437
5495
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5438
5496
|
*
|
|
5439
5497
|
* @example
|
|
@@ -5476,6 +5534,8 @@ var AuthClient = class {
|
|
|
5476
5534
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5477
5535
|
case 404:
|
|
5478
5536
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5537
|
+
case 409:
|
|
5538
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5479
5539
|
case 429:
|
|
5480
5540
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5481
5541
|
default:
|
|
@@ -5749,7 +5809,7 @@ var AuthClient = class {
|
|
|
5749
5809
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/issuers/{issuerId}");
|
|
5750
5810
|
}
|
|
5751
5811
|
/**
|
|
5752
|
-
* Updates the mutable fields of a registered issuer: `subClaim`, `emailClaim`, `userinfoUri`, `capturedClaims`, `restrictedToDomain` (a new non-blank value must already be a VERIFIED domain for your account; an empty string clears it
|
|
5812
|
+
* Updates the mutable fields of a registered issuer: `subClaim`, `emailClaim`, `userinfoUri`, `capturedClaims`, `restrictedToDomain` (a new non-blank value must already be a VERIFIED domain for your account; an empty string clears it where permitted; see `IssuerUpdateRequest.restrictedToDomain` for the full semantics), `status` (`active`/`suspended` — a registration awaiting verification (`pending_verification`) accepts no status change until it is verified; a suspended issuer's tokens are rejected identically to an unregistered issuer at exchange time), and `selfSignupPolicies`. Fields omitted from the body are left unchanged (partial update). `issuer`, `jwksUri`, `audience`, and `contextId` are trust-anchor / routing-pin fields and are immutable via this route — supplying a value that differs from the current registration is rejected with 400; supplying the current value back is a no-op. Rotating a trust anchor requires deleting and re-registering the issuer, which is itself refused while any user is bound through it. **`subClaim` is identity-determining, not merely cosmetic**: it names which verified JWT claim is read as the federated user's identifier, so changing it on an issuer that already has bound users would silently re-identify (or, under self-signup, orphan) every one of them — changing it is therefore refused once any user has bound through this issuer, the same guard `DELETE` already applies, and is only free before the first real login. Requires a root API key or the bootstrap's provisioning capability, gated identically to every other operation on this surface. A credential confined to one app context may only update an issuer registered in that context; naming one registered in another context returns 404, identically to a nonexistent issuerId. A root API key may update any issuer.
|
|
5753
5813
|
*
|
|
5754
5814
|
* @param {Vectros.IssuerUpdateRequest} request
|
|
5755
5815
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -5757,6 +5817,7 @@ var AuthClient = class {
|
|
|
5757
5817
|
* @throws {@link Vectros.BadRequestError}
|
|
5758
5818
|
* @throws {@link Vectros.ForbiddenError}
|
|
5759
5819
|
* @throws {@link Vectros.NotFoundError}
|
|
5820
|
+
* @throws {@link Vectros.ConflictError}
|
|
5760
5821
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5761
5822
|
*
|
|
5762
5823
|
* @example
|
|
@@ -5803,6 +5864,8 @@ var AuthClient = class {
|
|
|
5803
5864
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5804
5865
|
case 404:
|
|
5805
5866
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5867
|
+
case 409:
|
|
5868
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5806
5869
|
case 429:
|
|
5807
5870
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5808
5871
|
default:
|
|
@@ -5816,7 +5879,7 @@ var AuthClient = class {
|
|
|
5816
5879
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/v1/auth/issuers/{issuerId}");
|
|
5817
5880
|
}
|
|
5818
5881
|
/**
|
|
5819
|
-
* Deregisters a trusted third-party IdP issuer. Requires a root API key or the bootstrap's provisioning capability. A credential confined to one app context may only deregister an issuer registered in that context; naming one registered in another context returns 404, identically to a nonexistent issuerId. A root API key may deregister any issuer. Refused if any user account was ever created or matched via this issuer (by a prior self-signup or accepted invite, through `POST /v1/auth/token/exchange`) — that access cannot be silently orphaned. This is unconditional on the affected users' `status`: suspending them first does not lift the refusal. **A bound registration cannot be replaced, self-service, within its own app context**: registering a replacement issuer under a different issuerId in the SAME context also fails, because the context's one-active-issuer claim is released only when THIS registration is deleted. Two real options: suspend this issuer (stops new exchanges immediately) and register a replacement under a DIFFERENT app context — note that targets a different context, so this context's existing users/roles/access profiles are not carried over; or contact
|
|
5882
|
+
* Deregisters a trusted third-party IdP issuer. Requires a root API key or the bootstrap's provisioning capability. A credential confined to one app context may only deregister an issuer registered in that context; naming one registered in another context returns 404, identically to a nonexistent issuerId. A root API key may deregister any issuer. Refused if any user account was ever created or matched via this issuer (by a prior self-signup or accepted invite, through `POST /v1/auth/token/exchange`) — that access cannot be silently orphaned. This is unconditional on the affected users' `status`: suspending them first does not lift the refusal. **A bound registration cannot be replaced, self-service, within its own app context**: registering a replacement issuer under a different issuerId in the SAME context also fails, because the context's one-active-issuer claim is released only when THIS registration is deleted. Two real options: suspend this issuer (stops new exchanges immediately) and register a replacement under a DIFFERENT app context — note that targets a different context, so this context's existing users/roles/access profiles are not carried over; or contact support, who can force-release this registration so a replacement may be registered in the SAME context under a new issuerId. **The support path is not a lighter-weight alternative to the first — it PERMANENTLY RETIRES this issuerId and leaves every bound user unable to ever exchange through it again; it does not preserve continuity for them any better than registering under a different context does.** An issuer that has never been used for an exchange (no bound users yet) can always be deregistered.
|
|
5820
5883
|
*
|
|
5821
5884
|
* @param {Vectros.DeleteIssuerRequest} request
|
|
5822
5885
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -5942,7 +6005,7 @@ var AuthClient = class {
|
|
|
5942
6005
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/issuers");
|
|
5943
6006
|
}
|
|
5944
6007
|
/**
|
|
5945
|
-
* Registers a trusted third-party IdP issuer that BYO-IdP token exchange (`POST /v1/auth/token/exchange`) may accept a `subject_token` from. Requires a root API key or the CLI bootstrap's provisioning capability — never an ordinary
|
|
6008
|
+
* Registers a trusted third-party IdP issuer that BYO-IdP token exchange (`POST /v1/auth/token/exchange`) may accept a `subject_token` from. Requires a root API key or the CLI bootstrap's provisioning capability — never an ordinary grantable scope. A credential authorized only via the provisioning capability may register only against the app context it is bound to; naming a different one returns 403. A root API key is unaffected and may register against any of its contexts. A registration without `restrictedToDomain` is created `pending_verification` with a one-time challenge (`verificationClaim`, `verificationNonce`, `verificationExpiresAt`) and accepts no token until you prove you control the issuer with `POST /v1/auth/issuers/{issuerId}/verify`; it claims no `(issuer, audience)` pair until then, so registering never reveals whether another tenant holds one. A registration scoped to an already-verified domain with `restrictedToDomain` is `active` at once. Idempotent by `issuerId` within your tenant. If `issuerId` collides with a registration owned by a different app context than the one you're confined to, the request fails with 400 rather than returning that context's configuration. An app context may have at most one active issuer — deregister the existing one first if you need to replace it. One issuer MAY serve several contexts, each via its own registration row with a distinct `audience`. Optionally name `restrictedToDomain` to scope this registration's (issuer, audience) uniqueness to a specific VERIFIED domain rather than the bare pair — see that field's own description. A domain-scoped registration proves control of the DOMAIN, not of the issuer itself: it routes only tokens carrying that domain's `hd` claim.
|
|
5946
6009
|
*
|
|
5947
6010
|
* @param {Vectros.IssuerRequest} request
|
|
5948
6011
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6351,6 +6414,7 @@ var AuthClient = class {
|
|
|
6351
6414
|
* @throws {@link Vectros.BadRequestError}
|
|
6352
6415
|
* @throws {@link Vectros.ForbiddenError}
|
|
6353
6416
|
* @throws {@link Vectros.NotFoundError}
|
|
6417
|
+
* @throws {@link Vectros.ConflictError}
|
|
6354
6418
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
6355
6419
|
*
|
|
6356
6420
|
* @example
|
|
@@ -6398,6 +6462,8 @@ var AuthClient = class {
|
|
|
6398
6462
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
6399
6463
|
case 404:
|
|
6400
6464
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
6465
|
+
case 409:
|
|
6466
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
6401
6467
|
case 429:
|
|
6402
6468
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
6403
6469
|
default:
|
|
@@ -6497,6 +6563,7 @@ var AuthClient = class {
|
|
|
6497
6563
|
* @throws {@link Vectros.UnauthorizedError}
|
|
6498
6564
|
* @throws {@link Vectros.ForbiddenError}
|
|
6499
6565
|
* @throws {@link Vectros.NotFoundError}
|
|
6566
|
+
* @throws {@link Vectros.ContentTooLargeError}
|
|
6500
6567
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
6501
6568
|
*
|
|
6502
6569
|
* @example
|
|
@@ -6541,6 +6608,8 @@ var AuthClient = class {
|
|
|
6541
6608
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
6542
6609
|
case 404:
|
|
6543
6610
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
6611
|
+
case 413:
|
|
6612
|
+
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
6544
6613
|
case 429:
|
|
6545
6614
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
6546
6615
|
default:
|
|
@@ -6559,7 +6628,7 @@ var ComplianceClient = class {
|
|
|
6559
6628
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
6560
6629
|
}
|
|
6561
6630
|
/**
|
|
6562
|
-
* Submits
|
|
6631
|
+
* Submits an erasure request for a single end-subject (a user, or an identity entity in any namespace). Erasure deletes the records, documents, folders and record schemas the subject solely owns across the declared contexts (and, for a user, its access profiles there), plus its identity and lookup rows when all contexts are covered. Rows co-owned with another owner scope or principal are kept; read-access log entries are not erased. The request is asynchronous: it returns 202 with a `requestId`; poll `GET /v1/erasure-requests/{id}` until the job completes to obtain the completion certificate. Requires a root API key — a scoped credential is rejected with 403.
|
|
6563
6632
|
*
|
|
6564
6633
|
* @param {Vectros.ErasureRequest} request
|
|
6565
6634
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6622,7 +6691,7 @@ var ComplianceClient = class {
|
|
|
6622
6691
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/erasure-requests");
|
|
6623
6692
|
}
|
|
6624
6693
|
/**
|
|
6625
|
-
* Polls an erasure request by id. While the job is still running this returns its status only; once it completes, the response also includes the
|
|
6694
|
+
* Polls an erasure request by id. While the job is still running this returns its status only; once it completes, the response also includes the completion certificate (which contexts were swept, per-context deletion counts, whether the identity was kept, and reports of dangling references and shared rows left intact). Requires a root API key.
|
|
6626
6695
|
*
|
|
6627
6696
|
* @param {Vectros.GetErasureRequestRequest} request
|
|
6628
6697
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6680,7 +6749,7 @@ var ComplianceClient = class {
|
|
|
6680
6749
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/erasure-requests/{id}");
|
|
6681
6750
|
}
|
|
6682
6751
|
/**
|
|
6683
|
-
*
|
|
6752
|
+
* Reserved: with a root API key this operation answers `501` and starts no job. The request and response shapes are published so that integrations can be written against them: a request names the contexts, or a single end-subject, to export, and the response carries an `exportJobId` that `GET /v1/admin/export/{id}` reports on. Requires a root API key — a scoped credential is rejected with 403.
|
|
6684
6753
|
*
|
|
6685
6754
|
* @param {Vectros.ExportRequest} request
|
|
6686
6755
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6741,7 +6810,7 @@ var ComplianceClient = class {
|
|
|
6741
6810
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/admin/export");
|
|
6742
6811
|
}
|
|
6743
6812
|
/**
|
|
6744
|
-
*
|
|
6813
|
+
* Reserved: with a root API key this operation answers `501`. Its response shape is the one the export operation above refers to: the job status, and a short-lived presigned download URL and a manifest (format version and per-context counts) for a completed job. Requires a root API key.
|
|
6745
6814
|
*
|
|
6746
6815
|
* @param {Vectros.GetExportRequest} request
|
|
6747
6816
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6872,6 +6941,8 @@ var DocumentsClient = class {
|
|
|
6872
6941
|
*
|
|
6873
6942
|
* @throws {@link Vectros.BadRequestError}
|
|
6874
6943
|
* @throws {@link Vectros.ForbiddenError}
|
|
6944
|
+
* @throws {@link Vectros.NotFoundError}
|
|
6945
|
+
* @throws {@link Vectros.ConflictError}
|
|
6875
6946
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
6876
6947
|
*
|
|
6877
6948
|
* @example
|
|
@@ -6923,6 +6994,10 @@ var DocumentsClient = class {
|
|
|
6923
6994
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
6924
6995
|
case 403:
|
|
6925
6996
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
6997
|
+
case 404:
|
|
6998
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
6999
|
+
case 409:
|
|
7000
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
6926
7001
|
case 429:
|
|
6927
7002
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
6928
7003
|
default:
|
|
@@ -7070,6 +7145,7 @@ var DocumentsClient = class {
|
|
|
7070
7145
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
7071
7146
|
*
|
|
7072
7147
|
* @throws {@link Vectros.NotFoundError}
|
|
7148
|
+
* @throws {@link Vectros.ConflictError}
|
|
7073
7149
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7074
7150
|
*
|
|
7075
7151
|
* @example
|
|
@@ -7109,6 +7185,8 @@ var DocumentsClient = class {
|
|
|
7109
7185
|
switch (_response.error.statusCode) {
|
|
7110
7186
|
case 404:
|
|
7111
7187
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7188
|
+
case 409:
|
|
7189
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7112
7190
|
case 429:
|
|
7113
7191
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7114
7192
|
default:
|
|
@@ -7401,7 +7479,7 @@ var DocumentsClient = class {
|
|
|
7401
7479
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/download");
|
|
7402
7480
|
}
|
|
7403
7481
|
/**
|
|
7404
|
-
* Returns the document's full text body when it is retained:
|
|
7482
|
+
* Returns the document's full text body when it is retained: retained for text-ingested documents, and for file-uploaded documents unless they were uploaded with `storeText=false` (which discards the extracted text once indexing completes — the original file remains available via `GET /v1/documents/{id}/download`). Returns 404 when the document does not exist, its text was not retained, or extraction has not yet completed. Requires the `documents:r` scope.
|
|
7405
7483
|
*
|
|
7406
7484
|
* @param {Vectros.GetDocumentTextRequest} request
|
|
7407
7485
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7521,6 +7599,7 @@ var DocumentsClient = class {
|
|
|
7521
7599
|
*
|
|
7522
7600
|
* @throws {@link Vectros.BadRequestError}
|
|
7523
7601
|
* @throws {@link Vectros.ForbiddenError}
|
|
7602
|
+
* @throws {@link Vectros.ConflictError}
|
|
7524
7603
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7525
7604
|
*
|
|
7526
7605
|
* @example
|
|
@@ -7570,6 +7649,8 @@ var DocumentsClient = class {
|
|
|
7570
7649
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7571
7650
|
case 403:
|
|
7572
7651
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7652
|
+
case 409:
|
|
7653
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7573
7654
|
case 429:
|
|
7574
7655
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7575
7656
|
default:
|
|
@@ -7653,6 +7734,8 @@ var FoldersClient = class {
|
|
|
7653
7734
|
*
|
|
7654
7735
|
* @throws {@link Vectros.BadRequestError}
|
|
7655
7736
|
* @throws {@link Vectros.ForbiddenError}
|
|
7737
|
+
* @throws {@link Vectros.NotFoundError}
|
|
7738
|
+
* @throws {@link Vectros.ConflictError}
|
|
7656
7739
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7657
7740
|
*
|
|
7658
7741
|
* @example
|
|
@@ -7702,6 +7785,10 @@ var FoldersClient = class {
|
|
|
7702
7785
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7703
7786
|
case 403:
|
|
7704
7787
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7788
|
+
case 404:
|
|
7789
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7790
|
+
case 409:
|
|
7791
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7705
7792
|
case 429:
|
|
7706
7793
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7707
7794
|
default:
|
|
@@ -7777,6 +7864,7 @@ var FoldersClient = class {
|
|
|
7777
7864
|
*
|
|
7778
7865
|
* @throws {@link Vectros.ForbiddenError}
|
|
7779
7866
|
* @throws {@link Vectros.NotFoundError}
|
|
7867
|
+
* @throws {@link Vectros.ConflictError}
|
|
7780
7868
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7781
7869
|
*
|
|
7782
7870
|
* @example
|
|
@@ -7824,6 +7912,8 @@ var FoldersClient = class {
|
|
|
7824
7912
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7825
7913
|
case 404:
|
|
7826
7914
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7915
|
+
case 409:
|
|
7916
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7827
7917
|
case 429:
|
|
7828
7918
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7829
7919
|
default:
|
|
@@ -7844,6 +7934,7 @@ var FoldersClient = class {
|
|
|
7844
7934
|
*
|
|
7845
7935
|
* @throws {@link Vectros.BadRequestError}
|
|
7846
7936
|
* @throws {@link Vectros.NotFoundError}
|
|
7937
|
+
* @throws {@link Vectros.ConflictError}
|
|
7847
7938
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7848
7939
|
*
|
|
7849
7940
|
* @example
|
|
@@ -7885,6 +7976,8 @@ var FoldersClient = class {
|
|
|
7885
7976
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7886
7977
|
case 404:
|
|
7887
7978
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7979
|
+
case 409:
|
|
7980
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7888
7981
|
case 429:
|
|
7889
7982
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7890
7983
|
default:
|
|
@@ -7898,7 +7991,7 @@ var FoldersClient = class {
|
|
|
7898
7991
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/folders/{id}");
|
|
7899
7992
|
}
|
|
7900
7993
|
/**
|
|
7901
|
-
* Partially updates a folder using an RFC 7386 JSON Merge Patch. The `name`, `description`, and ownership fields (`userId`, `scopes`) are applied when present and left unchanged when omitted; sending any of these as null is rejected, because clearing a field is not supported
|
|
7994
|
+
* Partially updates a folder using an RFC 7386 JSON Merge Patch. The `name`, `description`, and ownership fields (`userId`, `scopes`) are applied when present and left unchanged when omitted; sending any of these as null is rejected, because clearing a field is not supported (omit it instead). `slug` and `parentFolderId` are immutable — a folder cannot be re-slugged or moved via the API — and the request is rejected if either is present. Pass `expectedVersion` for optimistic concurrency (you get a 409 if the folder changed since you last read it). Requires the `folders:u` scope.
|
|
7902
7995
|
*
|
|
7903
7996
|
* @param {Vectros.PatchFolderRequest} request
|
|
7904
7997
|
* @param {FoldersClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -8121,6 +8214,8 @@ var IdentityClient = class {
|
|
|
8121
8214
|
*
|
|
8122
8215
|
* @throws {@link Vectros.BadRequestError}
|
|
8123
8216
|
* @throws {@link Vectros.ForbiddenError}
|
|
8217
|
+
* @throws {@link Vectros.NotFoundError}
|
|
8218
|
+
* @throws {@link Vectros.ConflictError}
|
|
8124
8219
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8125
8220
|
*
|
|
8126
8221
|
* @example
|
|
@@ -8173,6 +8268,10 @@ var IdentityClient = class {
|
|
|
8173
8268
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
8174
8269
|
case 403:
|
|
8175
8270
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
8271
|
+
case 404:
|
|
8272
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8273
|
+
case 409:
|
|
8274
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8176
8275
|
case 429:
|
|
8177
8276
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8178
8277
|
default:
|
|
@@ -8253,6 +8352,7 @@ var IdentityClient = class {
|
|
|
8253
8352
|
*
|
|
8254
8353
|
* @throws {@link Vectros.BadRequestError}
|
|
8255
8354
|
* @throws {@link Vectros.NotFoundError}
|
|
8355
|
+
* @throws {@link Vectros.ConflictError}
|
|
8256
8356
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8257
8357
|
*
|
|
8258
8358
|
* @example
|
|
@@ -8305,6 +8405,8 @@ var IdentityClient = class {
|
|
|
8305
8405
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
8306
8406
|
case 404:
|
|
8307
8407
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8408
|
+
case 409:
|
|
8409
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8308
8410
|
case 429:
|
|
8309
8411
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8310
8412
|
default:
|
|
@@ -8324,6 +8426,7 @@ var IdentityClient = class {
|
|
|
8324
8426
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
8325
8427
|
*
|
|
8326
8428
|
* @throws {@link Vectros.NotFoundError}
|
|
8429
|
+
* @throws {@link Vectros.ConflictError}
|
|
8327
8430
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8328
8431
|
*
|
|
8329
8432
|
* @example
|
|
@@ -8368,6 +8471,8 @@ var IdentityClient = class {
|
|
|
8368
8471
|
switch (_response.error.statusCode) {
|
|
8369
8472
|
case 404:
|
|
8370
8473
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8474
|
+
case 409:
|
|
8475
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8371
8476
|
case 429:
|
|
8372
8477
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8373
8478
|
default:
|
|
@@ -8591,6 +8696,7 @@ var IdentityClient = class {
|
|
|
8591
8696
|
*
|
|
8592
8697
|
* @throws {@link Vectros.ForbiddenError}
|
|
8593
8698
|
* @throws {@link Vectros.NotFoundError}
|
|
8699
|
+
* @throws {@link Vectros.ConflictError}
|
|
8594
8700
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8595
8701
|
*
|
|
8596
8702
|
* @example
|
|
@@ -8642,6 +8748,8 @@ var IdentityClient = class {
|
|
|
8642
8748
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
8643
8749
|
case 404:
|
|
8644
8750
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8751
|
+
case 409:
|
|
8752
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8645
8753
|
case 429:
|
|
8646
8754
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8647
8755
|
default:
|
|
@@ -8662,6 +8770,7 @@ var IdentityClient = class {
|
|
|
8662
8770
|
*
|
|
8663
8771
|
* @throws {@link Vectros.ForbiddenError}
|
|
8664
8772
|
* @throws {@link Vectros.NotFoundError}
|
|
8773
|
+
* @throws {@link Vectros.ConflictError}
|
|
8665
8774
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8666
8775
|
*
|
|
8667
8776
|
* @example
|
|
@@ -8706,6 +8815,8 @@ var IdentityClient = class {
|
|
|
8706
8815
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
8707
8816
|
case 404:
|
|
8708
8817
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8818
|
+
case 409:
|
|
8819
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8709
8820
|
case 429:
|
|
8710
8821
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8711
8822
|
default:
|
|
@@ -8770,7 +8881,7 @@ var IdentityClient = class {
|
|
|
8770
8881
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/namespaces");
|
|
8771
8882
|
}
|
|
8772
8883
|
/**
|
|
8773
|
-
* Registers a new scope namespace and declares whether its values resolve to identity entities (`entityBacked`). Also requires `specificityRank`, an explicit, account-unique position in the specificity order used to break recordType schema-resolution ties. Requires a root API key or one of the CLI bootstrap's two provisioning capabilities — never an ordinary
|
|
8884
|
+
* Registers a new scope namespace and declares whether its values resolve to identity entities (`entityBacked`). Also requires `specificityRank`, an explicit, account-unique position in the specificity order used to break recordType schema-resolution ties. Requires a root API key or one of the CLI bootstrap's two provisioning capabilities — never an ordinary grantable scope. A bootstrap credential's registration is confined to its own app context UNLESS it additionally holds the tenant-wide namespace-provisioning capability and `contextId` is omitted, in which case it may register a TENANT-WIDE namespace (visible to every context). `org` and `client` are reserved names, registered the same way as any other namespace.
|
|
8774
8885
|
*
|
|
8775
8886
|
* @param {Vectros.RegisterNamespaceRequest} request
|
|
8776
8887
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -8912,6 +9023,7 @@ var IdentityClient = class {
|
|
|
8912
9023
|
*
|
|
8913
9024
|
* @throws {@link Vectros.BadRequestError}
|
|
8914
9025
|
* @throws {@link Vectros.ForbiddenError}
|
|
9026
|
+
* @throws {@link Vectros.ConflictError}
|
|
8915
9027
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8916
9028
|
*
|
|
8917
9029
|
* @example
|
|
@@ -8961,6 +9073,8 @@ var IdentityClient = class {
|
|
|
8961
9073
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
8962
9074
|
case 403:
|
|
8963
9075
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
9076
|
+
case 409:
|
|
9077
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8964
9078
|
case 429:
|
|
8965
9079
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8966
9080
|
default:
|
|
@@ -9029,13 +9143,14 @@ var IdentityClient = class {
|
|
|
9029
9143
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/users/{id}");
|
|
9030
9144
|
}
|
|
9031
9145
|
/**
|
|
9032
|
-
* 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. **`externalSubject` set this way is NOT an authentication binding and does not enable sign-in anywhere** — not the Vectros DevPortal/Admin App/web apps, and not `/v1/auth/token/exchange` either; both are granted only by the invitee authenticating themselves. If the invitee needs dev-portal/web-app access, send them the invitation link and let them accept it directly. If the invitee needs to sign in via token exchange, have them redeem `inviteToken` themselves at `/v1/auth/token/exchange` with their own credential — Vectros verifies it there and computes its own value for this field from the verified credential. The value you send here is stored exactly as-is, with no normalization, and does NOT automatically match or deduplicate against what a later token-exchange sign-in computes for the same real-world identity. Call this endpoint directly only if you run your own backend and want Vectros to record which of your own users a given account corresponds to — treat this field as informational for that purpose, not as a way to pre-authorize sign-in. Requires the `users:u` scope.
|
|
9146
|
+
* 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. **`externalSubject` set this way is NOT an authentication binding and does not enable sign-in anywhere** — not the Vectros DevPortal/Admin App/web apps, and not `/v1/auth/token/exchange` either; both are granted only by the invitee authenticating themselves. A subject stored through `PUT /v1/users/{id}` does not make the person a member of your account: it is ignored for the tenant claims in sign-in tokens, the developer portal's memberships list and tenant switching, and an ownership transfer. To give such a person access, delete the user and invite them again; accepting the invitation binds the subject and counts it. If the invitee needs dev-portal/web-app access, send them the invitation link and let them accept it directly. If the invitee needs to sign in via token exchange, have them redeem `inviteToken` themselves at `/v1/auth/token/exchange` with their own credential — Vectros verifies it there and computes its own value for this field from the verified credential. The value you send here is stored exactly as-is, with no normalization, and does NOT automatically match or deduplicate against what a later token-exchange sign-in computes for the same real-world identity. Call this endpoint directly only if you run your own backend and want Vectros to record which of your own users a given account corresponds to — treat this field as informational for that purpose, not as a way to pre-authorize sign-in. Requires the `users:u` scope.
|
|
9033
9147
|
*
|
|
9034
9148
|
* @param {Vectros.UpdateUserRequest} request
|
|
9035
9149
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
9036
9150
|
*
|
|
9037
9151
|
* @throws {@link Vectros.BadRequestError}
|
|
9038
9152
|
* @throws {@link Vectros.NotFoundError}
|
|
9153
|
+
* @throws {@link Vectros.ConflictError}
|
|
9039
9154
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
9040
9155
|
*
|
|
9041
9156
|
* @example
|
|
@@ -9083,6 +9198,8 @@ var IdentityClient = class {
|
|
|
9083
9198
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
9084
9199
|
case 404:
|
|
9085
9200
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
9201
|
+
case 409:
|
|
9202
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
9086
9203
|
case 429:
|
|
9087
9204
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9088
9205
|
default:
|
|
@@ -9344,7 +9461,7 @@ var InferenceClient = class {
|
|
|
9344
9461
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
9345
9462
|
}
|
|
9346
9463
|
/**
|
|
9347
|
-
* Returns
|
|
9464
|
+
* Returns the whole platform model catalog, whatever your plan: `availableOn` names each model's plan tiers. Each entry also carries the context window and the credit rates per 1K input and output tokens: `regionPricing.base` for global-region requests, `regionPricing.us` for default requests, and `longContext` for prompts over its threshold. A model reached through a `providerAlias` is not listed. Use this to populate model pickers and to validate a request before calling `/v1/chat`, `/v1/rag`, or `/v1/documents/{id}/ask`.
|
|
9348
9465
|
*
|
|
9349
9466
|
* @param {InferenceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
9350
9467
|
*
|
|
@@ -9388,7 +9505,7 @@ var InferenceClient = class {
|
|
|
9388
9505
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/models");
|
|
9389
9506
|
}
|
|
9390
9507
|
/**
|
|
9391
|
-
* Streams a model response as Server-Sent Events (SSE). Send the full conversation history in the `messages` array; a message with role `system` is extracted and used as the system prompt.
|
|
9508
|
+
* Streams a model response as Server-Sent Events (SSE). Send the full conversation history in the `messages` array; a message with role `system` is extracted and used as the system prompt. On a pre-paid account, token cost is debited from your inference balance; usage-billed accounts accrue it for post-billing. A request routed through your own `providerAlias` incurs no Vectros token cost. A small per-call flat fee is charged to your monthly credit allowance, except when the call fails on the server side. Requires the `inference:r` scope.
|
|
9392
9509
|
*/
|
|
9393
9510
|
chatInference(request, requestOptions) {
|
|
9394
9511
|
return HttpResponsePromise.fromPromise(this.__chatInference(request, requestOptions));
|
|
@@ -9441,8 +9558,14 @@ var InferenceClient = class {
|
|
|
9441
9558
|
throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
|
|
9442
9559
|
case 403:
|
|
9443
9560
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
9561
|
+
case 404:
|
|
9562
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
9563
|
+
case 413:
|
|
9564
|
+
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
9444
9565
|
case 429:
|
|
9445
9566
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9567
|
+
case 503:
|
|
9568
|
+
throw new ServiceUnavailableError(_response.error.body, _response.rawResponse);
|
|
9446
9569
|
default:
|
|
9447
9570
|
throw new VectrosError({
|
|
9448
9571
|
statusCode: _response.error.statusCode,
|
|
@@ -9516,6 +9639,8 @@ var InferenceClient = class {
|
|
|
9516
9639
|
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
9517
9640
|
case 429:
|
|
9518
9641
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9642
|
+
case 503:
|
|
9643
|
+
throw new ServiceUnavailableError(_response.error.body, _response.rawResponse);
|
|
9519
9644
|
default:
|
|
9520
9645
|
throw new VectrosError({
|
|
9521
9646
|
statusCode: _response.error.statusCode,
|
|
@@ -9580,10 +9705,16 @@ var InferenceClient = class {
|
|
|
9580
9705
|
throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
|
|
9581
9706
|
case 403:
|
|
9582
9707
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
9708
|
+
case 404:
|
|
9709
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
9710
|
+
case 413:
|
|
9711
|
+
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
9583
9712
|
case 429:
|
|
9584
9713
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9585
9714
|
case 500:
|
|
9586
9715
|
throw new InternalServerError(_response.error.body, _response.rawResponse);
|
|
9716
|
+
case 503:
|
|
9717
|
+
throw new ServiceUnavailableError(_response.error.body, _response.rawResponse);
|
|
9587
9718
|
default:
|
|
9588
9719
|
throw new VectrosError({
|
|
9589
9720
|
statusCode: _response.error.statusCode,
|
|
@@ -9661,7 +9792,7 @@ var RecordsClient = class {
|
|
|
9661
9792
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch-get");
|
|
9662
9793
|
}
|
|
9663
9794
|
/**
|
|
9664
|
-
* Reserved endpoint for batch lookup and reference resolution. The published response shape correlates results to each input and carries a per-item status envelope.
|
|
9795
|
+
* Reserved endpoint for batch lookup and reference resolution. With the `records:r` scope it answers 501 (not implemented). The published response shape correlates results to each input and carries a per-item status envelope. Requires the `records:r` scope.
|
|
9665
9796
|
*
|
|
9666
9797
|
* @param {Vectros.BatchLookupRequest} request
|
|
9667
9798
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -9722,7 +9853,7 @@ var RecordsClient = class {
|
|
|
9722
9853
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/lookup/batch");
|
|
9723
9854
|
}
|
|
9724
9855
|
/**
|
|
9725
|
-
* Writes up to 50 records
|
|
9856
|
+
* Writes up to 50 records per request in `best_effort` mode and up to 50 with `atomicity: all_or_nothing`, in a single request. Each item in `items` has exactly the same shape, and goes through exactly the same validation, authorization and idempotency rules, as the body of a single `POST /v1/records` — including schema validation, `externalId` idempotency, unique-field enforcement, and the `records:c:<type>` scope check, which is applied per item against that item's own record type. A batch is not a way to write a type your credential could not write one at a time.
|
|
9726
9857
|
*
|
|
9727
9858
|
* `atomicity` selects how the batch commits. `best_effort` (the default) writes each item independently: some can succeed while others fail, and each item's own outcome is reported. `all_or_nothing` commits every item in one transaction — if any item fails, no record is created or updated at all, and every item that was itself fine reports `not_committed` while the ones that failed report why. Because the transaction is bounded by the number of underlying storage rows rather than by the number of records, a large `all_or_nothing` batch can be refused as too large to commit atomically even when it is within the item limit; the error says so, and nothing is written when it happens.
|
|
9728
9859
|
*
|
|
@@ -9861,6 +9992,8 @@ var RecordsClient = class {
|
|
|
9861
9992
|
*
|
|
9862
9993
|
* @throws {@link Vectros.BadRequestError}
|
|
9863
9994
|
* @throws {@link Vectros.ForbiddenError}
|
|
9995
|
+
* @throws {@link Vectros.NotFoundError}
|
|
9996
|
+
* @throws {@link Vectros.ConflictError}
|
|
9864
9997
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
9865
9998
|
*
|
|
9866
9999
|
* @example
|
|
@@ -9917,6 +10050,10 @@ var RecordsClient = class {
|
|
|
9917
10050
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
9918
10051
|
case 403:
|
|
9919
10052
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
10053
|
+
case 404:
|
|
10054
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
10055
|
+
case 409:
|
|
10056
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
9920
10057
|
case 429:
|
|
9921
10058
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9922
10059
|
default:
|
|
@@ -10065,6 +10202,7 @@ var RecordsClient = class {
|
|
|
10065
10202
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
10066
10203
|
*
|
|
10067
10204
|
* @throws {@link Vectros.NotFoundError}
|
|
10205
|
+
* @throws {@link Vectros.ConflictError}
|
|
10068
10206
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
10069
10207
|
*
|
|
10070
10208
|
* @example
|
|
@@ -10104,6 +10242,8 @@ var RecordsClient = class {
|
|
|
10104
10242
|
switch (_response.error.statusCode) {
|
|
10105
10243
|
case 404:
|
|
10106
10244
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
10245
|
+
case 409:
|
|
10246
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
10107
10247
|
case 429:
|
|
10108
10248
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
10109
10249
|
default:
|
|
@@ -10117,7 +10257,7 @@ var RecordsClient = class {
|
|
|
10117
10257
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/records/{id}");
|
|
10118
10258
|
}
|
|
10119
10259
|
/**
|
|
10120
|
-
* Partially updates a record using an RFC 7386 JSON Merge Patch. The `payload` object is deep-merged into the existing payload: keys you send overwrite (recursing into nested objects), a key set to null is deleted, and keys you omit are left unchanged — so you can change a single field without re-sending the rest (unlike the full-replacement PUT). Top-level fields (`status`, `folderId`, `userId`, `scopes`) are set when present and left unchanged when omitted; sending a top-level field as null is rejected (clearing a top-level field is not supported
|
|
10260
|
+
* Partially updates a record using an RFC 7386 JSON Merge Patch. The `payload` object is deep-merged into the existing payload: keys you send overwrite (recursing into nested objects), a key set to null is deleted, and keys you omit are left unchanged — so you can change a single field without re-sending the rest (unlike the full-replacement PUT). Top-level fields (`status`, `folderId`, `userId`, `scopes`) are set when present and left unchanged when omitted; sending a top-level field as null is rejected (clearing a top-level field is not supported — omit it instead). `typeName`, `schemaId`, `externalId`, and `indexMode` are immutable and rejected if present. The merged result is validated against the schema. Pass `expectedVersion` to make the patch conditional (optimistic concurrency, 409 on conflict). Requires the `records:u:<type>` scope.
|
|
10121
10261
|
*
|
|
10122
10262
|
* @param {Vectros.PatchRecordRequest} request
|
|
10123
10263
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -10546,6 +10686,8 @@ var SchemasClient = class {
|
|
|
10546
10686
|
*
|
|
10547
10687
|
* @throws {@link Vectros.BadRequestError}
|
|
10548
10688
|
* @throws {@link Vectros.ForbiddenError}
|
|
10689
|
+
* @throws {@link Vectros.NotFoundError}
|
|
10690
|
+
* @throws {@link Vectros.ConflictError}
|
|
10549
10691
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
10550
10692
|
*
|
|
10551
10693
|
* @example
|
|
@@ -10615,6 +10757,10 @@ var SchemasClient = class {
|
|
|
10615
10757
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
10616
10758
|
case 403:
|
|
10617
10759
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
10760
|
+
case 404:
|
|
10761
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
10762
|
+
case 409:
|
|
10763
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
10618
10764
|
case 429:
|
|
10619
10765
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
10620
10766
|
default:
|
|
@@ -10758,7 +10904,7 @@ var SchemasClient = class {
|
|
|
10758
10904
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/v1/schemas/{id}");
|
|
10759
10905
|
}
|
|
10760
10906
|
/**
|
|
10761
|
-
* Permanently deletes a record schema. The request is refused with 409 if records of this type still exist — delete those records first, since every record must reference a live schema. The same applies to documents bound to this schema (for a schema declaring the `document` surface) — delete those documents first. A lineage base (a schema other schemas declare `basedOn`) also cannot be deleted while any such variant still exists — delete the variant schema(s) first. It is likewise refused while any trigger rule fires off this schema — delete those trigger rules first. Requires the `schemas:d` scope.
|
|
10907
|
+
* Permanently deletes a record schema. The request is refused with 409 if records of this type still exist — delete those records first, since every record must reference a live schema. The same applies to documents bound to this schema (for a schema declaring the `document` surface) — delete those documents first. A lineage base (a schema other schemas declare `basedOn`) also cannot be deleted while any such variant still exists — delete the variant schema(s) first. It is likewise refused while any trigger rule fires off this schema — delete those trigger rules first — and, for a schema declaring the `user` or `entity` surface, while any user or entity bound to it still exists. Requires the `schemas:d` scope.
|
|
10762
10908
|
*
|
|
10763
10909
|
* @param {Vectros.DeleteSchemaRequest} request
|
|
10764
10910
|
* @param {SchemasClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -10885,7 +11031,7 @@ var ScriptsClient = class {
|
|
|
10885
11031
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
10886
11032
|
}
|
|
10887
11033
|
/**
|
|
10888
|
-
* Runs a script you previously pushed to `POST /v1/scripts`, under your own credential, and returns what it returned. Every write the script makes is buffered and committed as ONE transaction after the script finishes: either all of them land or none do, and the response is sent only after they have committed — so everything in `result` is committed state or the script's own computation over it. On any failure the body is the standard error envelope and nothing the script staged is committed: its rows are written only when the transaction commits, and a document body or a large payload is DECIDED at that point and WRITTEN to storage only after the commit, so an aborted execution leaves nothing behind. The one residual sits after a successful commit, outside this response: if storing a body fails then, the row exists and the body
|
|
11034
|
+
* Runs a script you previously pushed to `POST /v1/scripts`, under your own credential, and returns what it returned. Every write the script makes is buffered and committed as ONE transaction after the script finishes: either all of them land or none do, and the response is sent only after they have committed — so everything in `result` is committed state or the script's own computation over it. On any failure the body is the standard error envelope and nothing the script staged is committed: its rows are written only when the transaction commits, and a document body or a large payload is DECIDED at that point and WRITTEN to storage only after the commit, so an aborted execution leaves nothing behind. The one residual sits after a successful commit, outside this response: if storing a body fails then, the row exists and the body may not — so a `200` guarantees the rows, and a document's text should be read before it is relied on.
|
|
10889
11035
|
*
|
|
10890
11036
|
* **Permission.** Requires the `scripts:x` scope — the permission to *execute* — separate from the `scripts:c` permission to push scripts. Bare `scripts:x` grants every script in the context; `scripts:x:<name>` grants exactly that script name, every version of it. Execution discloses no source, so `scripts:r` is not needed. Inside the script, every read and write is enforced by your credential's data scopes exactly as the equivalent REST call would be — `scripts:x` alone can read and write nothing.
|
|
10891
11037
|
*
|
|
@@ -11499,6 +11645,8 @@ var TriggersClient = class {
|
|
|
11499
11645
|
*
|
|
11500
11646
|
* @throws {@link Vectros.BadRequestError}
|
|
11501
11647
|
* @throws {@link Vectros.ForbiddenError}
|
|
11648
|
+
* @throws {@link Vectros.NotFoundError}
|
|
11649
|
+
* @throws {@link Vectros.ConflictError}
|
|
11502
11650
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
11503
11651
|
*
|
|
11504
11652
|
* @example
|
|
@@ -11556,6 +11704,10 @@ var TriggersClient = class {
|
|
|
11556
11704
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
11557
11705
|
case 403:
|
|
11558
11706
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
11707
|
+
case 404:
|
|
11708
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
11709
|
+
case 409:
|
|
11710
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
11559
11711
|
case 429:
|
|
11560
11712
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
11561
11713
|
default:
|
|
@@ -11632,6 +11784,7 @@ var TriggersClient = class {
|
|
|
11632
11784
|
* @throws {@link Vectros.BadRequestError}
|
|
11633
11785
|
* @throws {@link Vectros.ForbiddenError}
|
|
11634
11786
|
* @throws {@link Vectros.NotFoundError}
|
|
11787
|
+
* @throws {@link Vectros.ConflictError}
|
|
11635
11788
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
11636
11789
|
*
|
|
11637
11790
|
* @example
|
|
@@ -11689,6 +11842,8 @@ var TriggersClient = class {
|
|
|
11689
11842
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
11690
11843
|
case 404:
|
|
11691
11844
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
11845
|
+
case 409:
|
|
11846
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
11692
11847
|
case 429:
|
|
11693
11848
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
11694
11849
|
default:
|
|
@@ -11708,6 +11863,7 @@ var TriggersClient = class {
|
|
|
11708
11863
|
* @param {TriggersClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
11709
11864
|
*
|
|
11710
11865
|
* @throws {@link Vectros.NotFoundError}
|
|
11866
|
+
* @throws {@link Vectros.ConflictError}
|
|
11711
11867
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
11712
11868
|
*
|
|
11713
11869
|
* @example
|
|
@@ -11747,6 +11903,8 @@ var TriggersClient = class {
|
|
|
11747
11903
|
switch (_response.error.statusCode) {
|
|
11748
11904
|
case 404:
|
|
11749
11905
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
11906
|
+
case 409:
|
|
11907
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
11750
11908
|
case 429:
|
|
11751
11909
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
11752
11910
|
default:
|
|
@@ -11877,7 +12035,7 @@ var RenderHintsSchema = import_zod.z.object({
|
|
|
11877
12035
|
helpText: import_zod.z.string().optional(),
|
|
11878
12036
|
// Marks this field as the record's headline (display) field — the linked
|
|
11879
12037
|
// primary column in the records list + the title on the detail view. At most
|
|
11880
|
-
// one per schema (the platform
|
|
12038
|
+
// one per schema (the platform stores the hint and does not enforce one; the reference UI uses the first by order). Format passthrough →
|
|
11881
12039
|
// SchemaRequest.renderHints[fieldId].displayField.
|
|
11882
12040
|
displayField: import_zod.z.boolean().optional()
|
|
11883
12041
|
}).strict();
|
|
@@ -12082,7 +12240,7 @@ var BlueprintSchemaSchema = import_zod.z.object({
|
|
|
12082
12240
|
// (`lintNamespacedScopeArrays`) — this `.max(MAX_SCOPE_DIMENSIONS)` is
|
|
12083
12241
|
// only the shape-level cap, not the grammar check.
|
|
12084
12242
|
// With a scoped token these must be consistent with the profile's dataScope
|
|
12085
|
-
// (
|
|
12243
|
+
// (the lints here check each entry's grammar only, not that consistency).
|
|
12086
12244
|
userId: import_zod.z.string().min(1).optional(),
|
|
12087
12245
|
scopes: import_zod.z.array(import_zod.z.string().min(1)).max(MAX_SCOPE_DIMENSIONS).optional(),
|
|
12088
12246
|
// The id of an existing schema this one is a CUSTOMIZATION of, when a schema
|
|
@@ -12448,8 +12606,8 @@ var IdentityDeclSchema = import_zod.z.object({
|
|
|
12448
12606
|
kind: IdentityKindSchema,
|
|
12449
12607
|
// safeDisplayString: `@vectros-ai/cli`'s `runBootstrapCli` and `blueprint plan` both print
|
|
12450
12608
|
// this RAW to stdout ("~ <name> (<kind>) — external id '<externalId>'"), immediately before
|
|
12451
|
-
// the "Apply this plan? [y/N]" confirmation gate itself — the highest-severity
|
|
12452
|
-
//
|
|
12609
|
+
// the "Apply this plan? [y/N]" confirmation gate itself — the highest-severity place to
|
|
12610
|
+
// close, since it sits at the confirmation prompt's own disclosure text, not merely
|
|
12453
12611
|
// somewhere upstream of it. `displayName` is wrapped too though not currently
|
|
12454
12612
|
// rendered in any preview (defense-in-depth, matching this file's practice for its adjacent
|
|
12455
12613
|
// sibling fields) — it does reach a platform API payload as an entity's `name`.
|
|
@@ -12795,7 +12953,7 @@ var SeedCommonShape = {
|
|
|
12795
12953
|
* seeds resolve a `reference` against (across surfaces: a record seed may
|
|
12796
12954
|
* reference a document seed by its externalId, and vice versa).
|
|
12797
12955
|
* `safeDisplayString`: not currently rendered in a plan-preview line (same as this schema's
|
|
12798
|
-
* `typeName` just above
|
|
12956
|
+
* `typeName` just above), but the same "costs nothing to close" standard
|
|
12799
12957
|
* applies — a short caller-supplied id, not free-form prose, so no YAML block-scalar
|
|
12800
12958
|
* collision risk like `description`'s.
|
|
12801
12959
|
*/
|
|
@@ -13460,10 +13618,10 @@ var BlueprintSchema = import_zod.z.object({
|
|
|
13460
13618
|
/** Stable blueprint id (the `--blueprint <name>` selector + idempotency key). */
|
|
13461
13619
|
name: safeDisplayString(import_zod.z.string().min(1)),
|
|
13462
13620
|
version: safeDisplayString(import_zod.z.string().min(1)),
|
|
13463
|
-
// NOT safeDisplayString — deliberately
|
|
13464
|
-
// blueprint
|
|
13621
|
+
// NOT safeDisplayString — deliberately: that wrap breaks real, multi-line-authored
|
|
13622
|
+
// blueprint `description`s. `description` is never interpolated into any
|
|
13465
13623
|
// rendered plan-preview line (`buildPlan`'s header prints
|
|
13466
|
-
// `name`/`version`/`contextId`, never `description`
|
|
13624
|
+
// `name`/`version`/`contextId`, never `description`), so
|
|
13467
13625
|
// wrapping it protected a sink that does not exist. It also actively broke real content:
|
|
13468
13626
|
// this is exactly the field authors reach for a YAML folded (`>`) or literal (`|`) block
|
|
13469
13627
|
// scalar to write, and clip chomping (the YAML default) always leaves exactly one trailing
|
|
@@ -13478,9 +13636,8 @@ var BlueprintSchema = import_zod.z.object({
|
|
|
13478
13636
|
/**
|
|
13479
13637
|
* Human-readable app-context name; defaults to `MCP — <name>` (see {@link contextNameOf})
|
|
13480
13638
|
* when absent. `safeDisplayString`: `contextNameOf` renders this raw into the FIRST line of
|
|
13481
|
-
* every `buildPlan` summary (the `context '...' (...)` step)
|
|
13482
|
-
*
|
|
13483
|
-
* `name`/`version`/`description` right above it.
|
|
13639
|
+
* every `buildPlan` summary (the `context '...' (...)` step); same shape as
|
|
13640
|
+
* `name`/`version` right above it.
|
|
13484
13641
|
*/
|
|
13485
13642
|
contextName: safeDisplayString(import_zod.z.string().min(1)).optional(),
|
|
13486
13643
|
/**
|
|
@@ -13536,7 +13693,8 @@ var BlueprintSchema = import_zod.z.object({
|
|
|
13536
13693
|
* alongside app-context/service-principal creation and issuers, BEFORE
|
|
13537
13694
|
* schemas/seed (an `entityBacked` namespace's entities, and an
|
|
13538
13695
|
* `allowedSurfaces: ['entity']` schema, both need the registration to
|
|
13539
|
-
* already exist). `org`/`client`
|
|
13696
|
+
* already exist). `org`/`client` are registered the same way as any other
|
|
13697
|
+
* namespace: signup does not register them, so declare an entry for each you use.
|
|
13540
13698
|
*/
|
|
13541
13699
|
namespaces: import_zod.z.array(BlueprintNamespaceSchema).optional(),
|
|
13542
13700
|
/**
|
|
@@ -13574,9 +13732,8 @@ var BlueprintSchema = import_zod.z.object({
|
|
|
13574
13732
|
*/
|
|
13575
13733
|
identityProjectionClaims: import_zod.z.array(
|
|
13576
13734
|
// safeDisplayString: rendered raw into buildPlan's context step (`identity projection:
|
|
13577
|
-
// [...]`) — same rendered-plan-preview shape as `capturedClaims` (its structural pair
|
|
13578
|
-
//
|
|
13579
|
-
// applies (safeDisplayString's own `.refine` before the blank-name `.refine` below,
|
|
13735
|
+
// [...]`) — same rendered-plan-preview shape as `capturedClaims` (its structural pair);
|
|
13736
|
+
// the same ordering rule applies (safeDisplayString's own `.refine` before the blank-name `.refine` below,
|
|
13580
13737
|
// both after `.max` since `.refine` returns a ZodEffects with no `.max`).
|
|
13581
13738
|
safeDisplayString(
|
|
13582
13739
|
import_zod.z.string().max(MAX_CAPTURED_CLAIM_NAME_LENGTH, {
|
|
@@ -13781,7 +13938,7 @@ function deriveSuffix(contextId) {
|
|
|
13781
13938
|
var WHOLE_TOKEN_RE = /^\$\{\{\s*([A-Za-z_]\w*)\.([A-Za-z_]\w*)\s*\}\}$/;
|
|
13782
13939
|
var TOKEN_SCAN_RE = /\$\$\{\{|\$\{\{([\s\S]*?)\}\}|\$\{\{/g;
|
|
13783
13940
|
var INNER_REF_RE = /^([A-Za-z_]\w*)\.([A-Za-z_]\w*)$/;
|
|
13784
|
-
var DEFERRED_NAMESPACES = /* @__PURE__ */ new Set(["self", "identities", "under", "member"]);
|
|
13941
|
+
var DEFERRED_NAMESPACES = /* @__PURE__ */ new Set(["self", "identities", "under", "member", "input"]);
|
|
13785
13942
|
var DEFERRED_BARE_TOKENS = /* @__PURE__ */ new Set(["any"]);
|
|
13786
13943
|
var DEFERRED_NS_ALTERNATION = [...DEFERRED_NAMESPACES].join("|");
|
|
13787
13944
|
var DEFERRED_INNER_RE = new RegExp(
|
|
@@ -16350,14 +16507,14 @@ var COGNITO_ALLOWED_HOSTS = Object.values(PER_ENV).map(
|
|
|
16350
16507
|
);
|
|
16351
16508
|
var BAKED_CLIENT_IDS = {
|
|
16352
16509
|
staging: "61hjvm25kkjb1eei8t2d3jkcl8",
|
|
16353
|
-
//
|
|
16510
|
+
// deployed staging hosted UI client
|
|
16354
16511
|
production: "63eipeedjv78q653k45niih46f"
|
|
16355
|
-
//
|
|
16512
|
+
// deployed production hosted UI client
|
|
16356
16513
|
};
|
|
16357
16514
|
var MissingCognitoClientIdError = class extends Error {
|
|
16358
16515
|
constructor(env) {
|
|
16359
16516
|
super(
|
|
16360
|
-
`No Cognito client id for '${env}'. Set VECTROS_COGNITO_CLIENT_ID
|
|
16517
|
+
`No Cognito client id for '${env}'. Set VECTROS_COGNITO_CLIENT_ID to the public client id of the sign-in app.`
|
|
16361
16518
|
);
|
|
16362
16519
|
this.name = "MissingCognitoClientIdError";
|
|
16363
16520
|
}
|
|
@@ -16500,6 +16657,25 @@ async function clearSession(baseDir) {
|
|
|
16500
16657
|
return false;
|
|
16501
16658
|
}
|
|
16502
16659
|
}
|
|
16660
|
+
var TokenEndpointError = class extends Error {
|
|
16661
|
+
status;
|
|
16662
|
+
/** The OAuth `error` field of a JSON error body (e.g. `invalid_grant`), when there is one. */
|
|
16663
|
+
oauthError;
|
|
16664
|
+
constructor(status, text) {
|
|
16665
|
+
super(`token endpoint HTTP ${status}${text ? ` \u2014 ${stripControlCharacters(text, 500)}` : ""}`);
|
|
16666
|
+
this.name = "TokenEndpointError";
|
|
16667
|
+
this.status = status;
|
|
16668
|
+
this.oauthError = oauthErrorOf(text);
|
|
16669
|
+
}
|
|
16670
|
+
};
|
|
16671
|
+
function oauthErrorOf(text) {
|
|
16672
|
+
try {
|
|
16673
|
+
const parsed = JSON.parse(text);
|
|
16674
|
+
return typeof parsed.error === "string" ? parsed.error : void 0;
|
|
16675
|
+
} catch {
|
|
16676
|
+
return void 0;
|
|
16677
|
+
}
|
|
16678
|
+
}
|
|
16503
16679
|
async function postToken(config, body, fetchImpl) {
|
|
16504
16680
|
const res = await fetchImpl(tokenEndpoint(config), {
|
|
16505
16681
|
method: "POST",
|
|
@@ -16507,9 +16683,7 @@ async function postToken(config, body, fetchImpl) {
|
|
|
16507
16683
|
body: body.toString()
|
|
16508
16684
|
});
|
|
16509
16685
|
const text = await res.text().catch(() => "");
|
|
16510
|
-
if (!res.ok)
|
|
16511
|
-
throw new Error(`token endpoint HTTP ${res.status}${text ? ` \u2014 ${stripControlCharacters(text, 500)}` : ""}`);
|
|
16512
|
-
}
|
|
16686
|
+
if (!res.ok) throw new TokenEndpointError(res.status, text);
|
|
16513
16687
|
try {
|
|
16514
16688
|
return JSON.parse(text);
|
|
16515
16689
|
} catch {
|
|
@@ -16525,32 +16699,56 @@ async function refreshTokens(config, refreshToken, fetchImpl = fetch) {
|
|
|
16525
16699
|
|
|
16526
16700
|
// src/auth-context.ts
|
|
16527
16701
|
var COGNITO_TOKEN_ENV = "VECTROS_BOOTSTRAP_TOKEN";
|
|
16702
|
+
function loginCommandFor(env) {
|
|
16703
|
+
return env === "staging" ? "vectros login --env staging" : "vectros login";
|
|
16704
|
+
}
|
|
16528
16705
|
var MissingCredentialError = class extends Error {
|
|
16529
|
-
constructor() {
|
|
16706
|
+
constructor(env) {
|
|
16530
16707
|
super(
|
|
16531
|
-
`No credential found.
|
|
16708
|
+
`No credential found. Run \`${loginCommandFor(env)}\` to sign in, or set ${COGNITO_TOKEN_ENV} or pass --token (generate a token in the developer portal).`
|
|
16532
16709
|
);
|
|
16533
16710
|
this.name = "MissingCredentialError";
|
|
16534
16711
|
}
|
|
16535
16712
|
};
|
|
16713
|
+
var SessionExpiredError = class extends Error {
|
|
16714
|
+
constructor(env) {
|
|
16715
|
+
super(
|
|
16716
|
+
`Your Vectros login session has expired or was revoked. Run \`${loginCommandFor(env)}\` to sign in again.`
|
|
16717
|
+
);
|
|
16718
|
+
this.name = "SessionExpiredError";
|
|
16719
|
+
}
|
|
16720
|
+
};
|
|
16536
16721
|
function resolveCognitoTokenSync(explicit) {
|
|
16537
16722
|
if (explicit && explicit.trim()) return explicit.trim();
|
|
16538
16723
|
const fromEnv = process.env[COGNITO_TOKEN_ENV];
|
|
16539
16724
|
if (fromEnv && fromEnv.trim()) return fromEnv.trim();
|
|
16540
16725
|
return void 0;
|
|
16541
16726
|
}
|
|
16542
|
-
async function resolveCognitoToken(env, explicit) {
|
|
16727
|
+
async function resolveCognitoToken(env, explicit, deps = { readSession, refreshTokens, saveSession }) {
|
|
16543
16728
|
const sync = resolveCognitoTokenSync(explicit);
|
|
16544
16729
|
if (sync) return sync;
|
|
16545
|
-
const session = await readSession();
|
|
16730
|
+
const session = await deps.readSession();
|
|
16546
16731
|
if (session && session.env === env) {
|
|
16547
16732
|
const config = resolveCognitoConfig(env);
|
|
16548
|
-
|
|
16733
|
+
let tokens;
|
|
16734
|
+
try {
|
|
16735
|
+
tokens = await deps.refreshTokens(config, session.refreshToken);
|
|
16736
|
+
} catch (err) {
|
|
16737
|
+
if (err instanceof TokenEndpointError && err.oauthError === "invalid_grant") throw new SessionExpiredError(env);
|
|
16738
|
+
throw err;
|
|
16739
|
+
}
|
|
16740
|
+
if (tokens.refresh_token && tokens.refresh_token !== session.refreshToken) {
|
|
16741
|
+
const stored = await deps.readSession().catch(() => void 0);
|
|
16742
|
+
if (stored?.refreshToken === session.refreshToken) {
|
|
16743
|
+
await deps.saveSession({ ...session, refreshToken: tokens.refresh_token }).catch(() => void 0);
|
|
16744
|
+
}
|
|
16745
|
+
}
|
|
16549
16746
|
const bearer = tokens.id_token ?? tokens.access_token;
|
|
16550
16747
|
if (bearer) return bearer;
|
|
16551
16748
|
}
|
|
16552
|
-
throw new MissingCredentialError();
|
|
16749
|
+
throw new MissingCredentialError(env);
|
|
16553
16750
|
}
|
|
16751
|
+
var DEFAULT_CONTEXT_ID = "default";
|
|
16554
16752
|
async function resolveAuthContext(opts) {
|
|
16555
16753
|
const baseUrl = envToBaseUrl(opts.env, opts.baseUrl ?? process.env.VECTROS_API_BASE_URL);
|
|
16556
16754
|
const cognitoToken = await resolveCognitoToken(opts.env, opts.token);
|
|
@@ -16560,7 +16758,7 @@ async function resolveAuthContext(opts) {
|
|
|
16560
16758
|
apiBaseUrl: baseUrl,
|
|
16561
16759
|
tenant,
|
|
16562
16760
|
...opts.context ? { contextId: opts.context } : {}
|
|
16563
|
-
}) : await mintContextToken({ cognitoToken, apiBaseUrl: baseUrl, contextId: opts.context ??
|
|
16761
|
+
}) : await mintContextToken({ cognitoToken, apiBaseUrl: baseUrl, contextId: opts.context ?? DEFAULT_CONTEXT_ID, tenant });
|
|
16564
16762
|
const client = new VectrosClient({ token: bridge.token, environment: baseUrl });
|
|
16565
16763
|
const identity = await resolveIdentity({ log: opts.log, apiKey: bridge.token, environment: baseUrl });
|
|
16566
16764
|
return {
|
|
@@ -17061,6 +17259,7 @@ function keyNameFor(pack, machineId) {
|
|
|
17061
17259
|
}
|
|
17062
17260
|
|
|
17063
17261
|
// src/identity-resolver.ts
|
|
17262
|
+
var IDENTITIES_TO_ENSURE_HEADING = " Identities to ensure (idempotent by external id; a non-user entity lives with its namespace):";
|
|
17064
17263
|
function userPayload(decl) {
|
|
17065
17264
|
const meta = decl.metadata;
|
|
17066
17265
|
if (decl.displayName === void 0) return meta;
|
|
@@ -17201,7 +17400,7 @@ async function withAuth(opts, deps, body) {
|
|
|
17201
17400
|
log
|
|
17202
17401
|
});
|
|
17203
17402
|
} catch (err) {
|
|
17204
|
-
if (err instanceof MissingCredentialError || err instanceof InvalidBaseUrlError || err instanceof InvalidCognitoRegionError) {
|
|
17403
|
+
if (err instanceof MissingCredentialError || err instanceof SessionExpiredError || err instanceof InvalidBaseUrlError || err instanceof InvalidCognitoRegionError) {
|
|
17205
17404
|
process.stderr.write(` \u2716 ${err.message}
|
|
17206
17405
|
`);
|
|
17207
17406
|
return 2;
|
|
@@ -17668,13 +17867,11 @@ function toSdkFields(blueprint, schemaIndex) {
|
|
|
17668
17867
|
searchable: f.searchable,
|
|
17669
17868
|
filterable: f.filterable,
|
|
17670
17869
|
// Forwarded explicitly, like every sibling here: this mapping is a named-property list, not a
|
|
17671
|
-
// spread, so an unforwarded field is dropped SILENTLY rather than failing
|
|
17672
|
-
// happened to `validation` before it was added below.
|
|
17870
|
+
// spread, so an unforwarded field is dropped SILENTLY rather than failing.
|
|
17673
17871
|
inline: f.inline,
|
|
17674
17872
|
description: f.description,
|
|
17675
17873
|
enumValues: mapEnumValues(f.enumValues),
|
|
17676
|
-
// Format passthrough
|
|
17677
|
-
// "smoking gun" toSdkFields mapped only six props). The platform
|
|
17874
|
+
// Format passthrough. The platform
|
|
17678
17875
|
// `SchemaRequest.FieldDef.validation` enforces these server-side. The
|
|
17679
17876
|
// typed blueprint ValidationRules narrows the SDK's free-form Record.
|
|
17680
17877
|
validation: f.validation,
|
|
@@ -17796,21 +17993,12 @@ async function provisionContext(client, input) {
|
|
|
17796
17993
|
const { blueprint, allowTenantWideNamespaces = false, readExistingKey, confirmExistingPrincipal, log } = input;
|
|
17797
17994
|
assertBlueprintGates(blueprint);
|
|
17798
17995
|
assertTenantWideNamespacesAllowed(blueprint, allowTenantWideNamespaces);
|
|
17799
|
-
const ctxResp = await client.auth.createAppContext({
|
|
17800
|
-
body: {
|
|
17801
|
-
contextId: blueprint.contextId,
|
|
17802
|
-
name: contextNameOf(blueprint),
|
|
17803
|
-
...blueprint.identityProjectionClaims === void 0 ? {} : { identityProjectionClaims: blueprint.identityProjectionClaims }
|
|
17804
|
-
}
|
|
17805
|
-
});
|
|
17806
|
-
const contextAction = actionOf(ctxResp.created);
|
|
17807
|
-
log.debug({ component: "loader", contextId: blueprint.contextId, contextAction }, "app-context ensured");
|
|
17808
17996
|
const user = await client.identity.createUser({
|
|
17809
17997
|
body: {
|
|
17810
17998
|
externalId: blueprint.servicePrincipal.externalId,
|
|
17811
17999
|
type: "SERVICE",
|
|
17812
18000
|
// SDK 0.18+ (identity-access rework): UserRequest's free-form attribute bag
|
|
17813
|
-
//
|
|
18001
|
+
// is `payload` (arbitrary key/value).
|
|
17814
18002
|
payload: { displayName: blueprint.servicePrincipal.displayName }
|
|
17815
18003
|
}
|
|
17816
18004
|
});
|
|
@@ -17845,6 +18033,15 @@ async function provisionContext(client, input) {
|
|
|
17845
18033
|
"existing-principal collision confirmed (or unwired) \u2014 proceeding with any further write to this principal"
|
|
17846
18034
|
);
|
|
17847
18035
|
}
|
|
18036
|
+
const ctxResp = await client.auth.createAppContext({
|
|
18037
|
+
body: {
|
|
18038
|
+
contextId: blueprint.contextId,
|
|
18039
|
+
name: contextNameOf(blueprint),
|
|
18040
|
+
...blueprint.identityProjectionClaims === void 0 ? {} : { identityProjectionClaims: blueprint.identityProjectionClaims }
|
|
18041
|
+
}
|
|
18042
|
+
});
|
|
18043
|
+
const contextAction = actionOf(ctxResp.created);
|
|
18044
|
+
log.debug({ component: "loader", contextId: blueprint.contextId, contextAction }, "app-context ensured");
|
|
17848
18045
|
const needsContextPinnedBootstrap = (blueprint.namespaces ?? []).length > 0;
|
|
17849
18046
|
const ctxBootstrapClient = needsContextPinnedBootstrap ? input.remintBootstrapForContext ? await input.remintBootstrapForContext(blueprint.contextId) : client : void 0;
|
|
17850
18047
|
if (ctxBootstrapClient && input.remintBootstrapForContext) {
|
|
@@ -17975,7 +18172,7 @@ async function runLoader(client, input) {
|
|
|
17975
18172
|
// them), so default to ['record'] when a blueprint omits it. The `as` keeps
|
|
17976
18173
|
// the literal default assignable to the SDK's enum-array type.
|
|
17977
18174
|
allowedSurfaces: s.allowedSurfaces ?? ["record"],
|
|
17978
|
-
// Format passthrough
|
|
18175
|
+
// Format passthrough. Ownership mirrors
|
|
17979
18176
|
// the SDK `SchemaRequest` shape 1:1: the principal `userId` plus `scopes`
|
|
17980
18177
|
// (namespaced parent edges `<ns>:<value>`; the flat orgId/clientId fields are gone).
|
|
17981
18178
|
renderHints: toRenderHintMap(s),
|
|
@@ -18353,14 +18550,14 @@ function renderPlanDataScope(ds) {
|
|
|
18353
18550
|
function buildPlan(blueprint, opts = {}) {
|
|
18354
18551
|
const steps = [];
|
|
18355
18552
|
const projection = blueprint.identityProjectionClaims?.length ? `, identity projection: [${blueprint.identityProjectionClaims.join(", ")}]` : blueprint.identityProjectionClaims ? ", identity projection: disabled (empty list)" : "";
|
|
18356
|
-
steps.push({
|
|
18357
|
-
kind: "context",
|
|
18358
|
-
summary: `context '${blueprint.contextId}' (${contextNameOf(blueprint)}${projection})`
|
|
18359
|
-
});
|
|
18360
18553
|
steps.push({
|
|
18361
18554
|
kind: "principal",
|
|
18362
18555
|
summary: `principal '${blueprint.servicePrincipal.externalId}' (SERVICE)`
|
|
18363
18556
|
});
|
|
18557
|
+
steps.push({
|
|
18558
|
+
kind: "context",
|
|
18559
|
+
summary: `context '${blueprint.contextId}' (${contextNameOf(blueprint)}${projection})`
|
|
18560
|
+
});
|
|
18364
18561
|
for (const ns of blueprint.namespaces ?? []) {
|
|
18365
18562
|
const ownerNote = ns.tenantWide ? "TENANT-WIDE \u2014 visible to every context" : `owned by '${blueprint.contextId}'`;
|
|
18366
18563
|
const entityNote = ns.entityBacked ? ", entity-backed" : "";
|
|
@@ -18826,10 +19023,8 @@ var OsKeychainSecretStore = class {
|
|
|
18826
19023
|
}
|
|
18827
19024
|
/**
|
|
18828
19025
|
* `present` or `unknown` — **never `absent`**: impossible through `getPassword`,
|
|
18829
|
-
* and held there by POLICY where other evidence exists
|
|
18830
|
-
*
|
|
18831
|
-
* tighten later", which the rest of this very docstring then disowns — the
|
|
18832
|
-
* widening procedure is spelled out below. State the real shape up front.)
|
|
19026
|
+
* and held there by POLICY where other evidence exists (the widening procedure
|
|
19027
|
+
* is spelled out below).
|
|
18833
19028
|
*
|
|
18834
19029
|
* Proving absence would require distinguishing "no such entry" from "I could not
|
|
18835
19030
|
* read it", and that information does not survive the binding: `get_password` is
|
|
@@ -18849,7 +19044,7 @@ var OsKeychainSecretStore = class {
|
|
|
18849
19044
|
* Linux collection `secret-tool` exits 1 while `getPassword` returns `null`, so
|
|
18850
19045
|
* the D-Bus layer reports the fault faithfully and `.ok()` is what discards it.
|
|
18851
19046
|
* A read with real error semantics could therefore make `absent` reachable — and
|
|
18852
|
-
* `find_credentials`
|
|
19047
|
+
* `find_credentials` HAS verified semantics on Linux: it throws
|
|
18853
19048
|
* `org.freedesktop.Secret.Error.IsLocked` against a locked collection (measured;
|
|
18854
19049
|
* see {@link OsKeychainSecretStore.deleteSecret} for the transcript). That is why
|
|
18855
19050
|
* `deleteSecret` may confirm a delete while this method may not.
|
|
@@ -18859,7 +19054,7 @@ var OsKeychainSecretStore = class {
|
|
|
18859
19054
|
* PRUNABLE, which is a behaviour change this CLI has not taken, and Windows'
|
|
18860
19055
|
* locked-store behaviour is unmeasured (it has no comparable lock state, so there
|
|
18861
19056
|
* is nothing yet to observe). Verify per-platform before widening; do not infer it
|
|
18862
|
-
* from a signature
|
|
19057
|
+
* from a signature.
|
|
18863
19058
|
*/
|
|
18864
19059
|
async presence(ref) {
|
|
18865
19060
|
try {
|
|
@@ -18890,10 +19085,8 @@ var OsKeychainSecretStore = class {
|
|
|
18890
19085
|
*
|
|
18891
19086
|
* MEASURED, not inferred — and the distinction is the whole point, because
|
|
18892
19087
|
* {@link OsKeychainSecretStore.presence} rightly forbids inferring a locked
|
|
18893
|
-
* store's behaviour from a `Result` signature
|
|
18894
|
-
*
|
|
18895
|
-
* precisely that, on the path that authorizes destruction. Being right by luck is
|
|
18896
|
-
* not being right. Against a real locked collection (`node:24-alpine`, non-root,
|
|
19088
|
+
* store's behaviour from a `Result` signature, and doing so on the path that
|
|
19089
|
+
* authorizes destruction would be right only by luck. Against a real locked collection (`node:24-alpine`, non-root,
|
|
18897
19090
|
* `dbus-run-session` + gnome-keyring, collection locked via
|
|
18898
19091
|
* `org.freedesktop.Secret.Service.Lock`):
|
|
18899
19092
|
*
|
|
@@ -18908,16 +19101,14 @@ var OsKeychainSecretStore = class {
|
|
|
18908
19101
|
* cached handle to it. CI cannot pin this half — it unlocks the collection so the rest of
|
|
18909
19102
|
* the suite can use it — so the evidence lives here rather than in a test.
|
|
18910
19103
|
*
|
|
18911
|
-
* The WINDOWS half of the same contract
|
|
18912
|
-
*
|
|
18913
|
-
* 2026-07-17): `findCredentials` enumerates `account` byte-for-byte as stored —
|
|
19104
|
+
* The WINDOWS half of the same contract is also measured (real Credential Manager,
|
|
19105
|
+
* real binding, throwaway service): `findCredentials` enumerates `account` byte-for-byte as stored —
|
|
18914
19106
|
* case preserved, 48-char refs intact — a confirmed delete empties the
|
|
18915
19107
|
* enumeration, and `deleteCredential` on a nonexistent ref returns the same
|
|
18916
19108
|
* meaningless `false` as Linux. So the `.some(c => c.account === ref)` comparison
|
|
18917
19109
|
* is sound on both measured platforms. Still unmeasured: a CI lane that runs this
|
|
18918
|
-
* against the real Windows store on every
|
|
18919
|
-
* measurement, not a regression gate.
|
|
18920
|
-
* public npm, so no tracker number here.)
|
|
19110
|
+
* against the real Windows store on every change — the dev-machine probe is a point
|
|
19111
|
+
* measurement, not a regression gate.
|
|
18921
19112
|
*
|
|
18922
19113
|
* ⚠️ THIS METHOD'S FAIL-CLOSED GUARANTEE IS AN UPSTREAM BEHAVIOURAL CONTRACT, NOT
|
|
18923
19114
|
* SOMETHING THIS CODE ENFORCES. `@napi-rs/keyring` is pinned to an EXACT version
|
|
@@ -18987,11 +19178,11 @@ var CryptoFileSecretStore = class {
|
|
|
18987
19178
|
/**
|
|
18988
19179
|
* The salt is the one file whose loss is UNRECOVERABLE — every `.enc` is scrypt'd
|
|
18989
19180
|
* against it, so replacing it silently re-keys the store and bricks them all. Two
|
|
18990
|
-
* rules follow
|
|
19181
|
+
* rules follow:
|
|
18991
19182
|
*
|
|
18992
19183
|
* - A present-but-short salt is treated exactly like a corrupt index
|
|
18993
19184
|
* ({@link readKeyring}): throw an actionable error, never silently replace.
|
|
18994
|
-
*
|
|
19185
|
+
* Regenerating it would convert one anomaly into every secret
|
|
18995
19186
|
* being permanently undecryptable — destruction dressed as repair.
|
|
18996
19187
|
* - Creation is temp + hard-link (atomic AND exclusive; below), with an
|
|
18997
19188
|
* exclusive-create (`wx`) fallback on no-hardlink filesystems. The link path
|
|
@@ -19073,7 +19264,7 @@ var CryptoFileSecretStore = class {
|
|
|
19073
19264
|
async setSecret(ref, secret) {
|
|
19074
19265
|
const key = await this.encryptionKey();
|
|
19075
19266
|
const iv = (0, import_node_crypto3.randomBytes)(IV_LEN);
|
|
19076
|
-
const cipher = (0, import_node_crypto3.createCipheriv)(ALGO, key, iv);
|
|
19267
|
+
const cipher = (0, import_node_crypto3.createCipheriv)(ALGO, key, iv, { authTagLength: TAG_LEN });
|
|
19077
19268
|
const enc = Buffer.concat([cipher.update(secret, "utf8"), cipher.final()]);
|
|
19078
19269
|
const tag = cipher.getAuthTag();
|
|
19079
19270
|
const blob = Buffer.concat([iv, tag, enc]);
|
|
@@ -19096,7 +19287,7 @@ var CryptoFileSecretStore = class {
|
|
|
19096
19287
|
const iv = blob.subarray(0, IV_LEN);
|
|
19097
19288
|
const tag = blob.subarray(IV_LEN, IV_LEN + TAG_LEN);
|
|
19098
19289
|
const enc = blob.subarray(IV_LEN + TAG_LEN);
|
|
19099
|
-
const decipher = (0, import_node_crypto3.createDecipheriv)(ALGO, key, iv);
|
|
19290
|
+
const decipher = (0, import_node_crypto3.createDecipheriv)(ALGO, key, iv, { authTagLength: TAG_LEN });
|
|
19100
19291
|
decipher.setAuthTag(tag);
|
|
19101
19292
|
return Buffer.concat([decipher.update(enc), decipher.final()]).toString("utf8");
|
|
19102
19293
|
} catch {
|
|
@@ -19142,9 +19333,8 @@ var CryptoFileSecretStore = class {
|
|
|
19142
19333
|
* EACCES, or the directory not being there AT ALL — answers false, i.e. "we know
|
|
19143
19334
|
* nothing".
|
|
19144
19335
|
*
|
|
19145
|
-
* A MISSING `secrets/` reads as "we know nothing", not as an honest empty
|
|
19146
|
-
*
|
|
19147
|
-
* looked right: nothing has ever been stored, so "no secret here" is true. Why it
|
|
19336
|
+
* A MISSING `secrets/` reads as "we know nothing", not as an honest empty. The
|
|
19337
|
+
* tempting reasoning — nothing has ever been stored, so "no secret here" is true —
|
|
19148
19338
|
* is wrong: this is only ever consulted about an entry that is IN the index and
|
|
19149
19339
|
* stamped `crypto-file` — meaning `setSecret` ran, and `setSecret` mkdir's
|
|
19150
19340
|
* `secrets/` before every write. So for the only entries that reach here, "the
|
|
@@ -19155,7 +19345,7 @@ var CryptoFileSecretStore = class {
|
|
|
19155
19345
|
* it went. Calling that `absent` invites `prune` to delete the entry and the only
|
|
19156
19346
|
* record of a keyId whose key is still live.
|
|
19157
19347
|
*
|
|
19158
|
-
* The fresh-install case
|
|
19348
|
+
* The fresh-install case needs no special handling: an install with nothing
|
|
19159
19349
|
* stored has no index entries either, so nothing asks.
|
|
19160
19350
|
*/
|
|
19161
19351
|
async secretsDirIntact() {
|
|
@@ -19272,10 +19462,9 @@ async function withIndexLock(dir, fn, timings = LOCK_OPTS) {
|
|
|
19272
19462
|
// Swallowing is the better answer. We cannot un-write what we already wrote;
|
|
19273
19463
|
// finishing is strictly better than crashing half-done.
|
|
19274
19464
|
//
|
|
19275
|
-
// What swallowing does NOT buy
|
|
19276
|
-
//
|
|
19277
|
-
//
|
|
19278
|
-
// delete a successor's lock. That detection runs on the mtime-refresh timer,
|
|
19465
|
+
// What swallowing does NOT buy is protection from our own release: the library
|
|
19466
|
+
// marks a lock released when it DETECTS a compromise, after which our release
|
|
19467
|
+
// below would be a no-op that cannot delete a successor's lock. That detection runs on the mtime-refresh timer,
|
|
19279
19468
|
// every `stale/2` — 2.5s at the shipping 5s — and this file's critical
|
|
19280
19469
|
// sections are ~1ms, so in practice the holder is gone before the first tick
|
|
19281
19470
|
// and nothing is ever detected. (The probe that observed the no-op ran at
|
|
@@ -19288,15 +19477,13 @@ async function withIndexLock(dir, fn, timings = LOCK_OPTS) {
|
|
|
19288
19477
|
// updateKeyring's identity re-check before each commit — which, per its own
|
|
19289
19478
|
// honesty note, narrows the lost-update window; it does not close it.
|
|
19290
19479
|
//
|
|
19291
|
-
// RESIDUAL, stated honestly
|
|
19292
|
-
//
|
|
19293
|
-
// compromised lock "requires our event loop to freeze past `stale`". It does
|
|
19294
|
-
// not. proper-lockfile's stale-break is `stat` -> judge stale -> `rmdir` ->
|
|
19480
|
+
// RESIDUAL, stated honestly: reaching a compromised lock does NOT require our
|
|
19481
|
+
// event loop to freeze past `stale`. proper-lockfile's stale-break is `stat` -> judge stale -> `rmdir` ->
|
|
19295
19482
|
// `mkdir`, and it never re-checks that the directory it removes is the one it
|
|
19296
19483
|
// judged: two waiters can both condemn one crash remnant, and the second's
|
|
19297
19484
|
// already-decided rmdir destroys the first's FRESH lock. Nothing freezes.
|
|
19298
19485
|
//
|
|
19299
|
-
// MEASURED against this exact config (
|
|
19486
|
+
// MEASURED against this exact config (probe results below):
|
|
19300
19487
|
// real OS processes, no injection : 0/25 and 1/25 (two independent runs)
|
|
19301
19488
|
// in-process, 5 waiters : ~3% per round
|
|
19302
19489
|
// rival's rmdir lagged one turn : 19/25
|
|
@@ -19308,10 +19495,10 @@ async function withIndexLock(dir, fn, timings = LOCK_OPTS) {
|
|
|
19308
19495
|
// round-trip. Rare, not impossible — and inherent to mtime-based locking of
|
|
19309
19496
|
// this shape.
|
|
19310
19497
|
//
|
|
19311
|
-
// Accepted KNOWINGLY.
|
|
19312
|
-
// behind a second mutex — the right idea —
|
|
19313
|
-
// breaker at 10s with no liveness check
|
|
19314
|
-
// injection at all, plus 0.57% of contended acquires dying on Windows EPERM.
|
|
19498
|
+
// Accepted KNOWINGLY. A hand-rolled lock that serialised the break
|
|
19499
|
+
// behind a second mutex — the right idea — hits a defect there: that mutex age-breaks a LIVE
|
|
19500
|
+
// breaker at 10s with no liveness check (`maxOccupancy = 2`, measured with no
|
|
19501
|
+
// injection at all), plus 0.57% of contended acquires dying on Windows EPERM.
|
|
19315
19502
|
// Trading a measured, reachable defect for one that needs injected lag to
|
|
19316
19503
|
// observe is the right trade; pretending it is zero is not. If it ever needs
|
|
19317
19504
|
// closing the fix is upstream (re-stat under the removal), NOT another
|
|
@@ -19441,8 +19628,8 @@ var Keyring = class _Keyring {
|
|
|
19441
19628
|
/**
|
|
19442
19629
|
* Where new secrets go, or undefined if the selected store isn't actually here.
|
|
19443
19630
|
*
|
|
19444
|
-
* That sounds impossible — the file store is always constructed —
|
|
19445
|
-
*
|
|
19631
|
+
* That sounds impossible — the file store is always constructed — so it is tempting
|
|
19632
|
+
* to cast the `undefined` away. That would be wrong:
|
|
19446
19633
|
* `new Keyring({ selection: { kind: 'os-keychain' } })` with no `keychainStore`
|
|
19447
19634
|
* names a store that was never supplied, and a cast does not conjure one. Return
|
|
19448
19635
|
* the honest type and let {@link storeSecretSomewhere} handle it, rather than
|
|
@@ -19620,7 +19807,7 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19620
19807
|
* {@link storeSecretSomewhere} throws — i.e. only when no store on this machine
|
|
19621
19808
|
* will accept a write at all, which is the failure worth stopping for.
|
|
19622
19809
|
*
|
|
19623
|
-
* Still NOT a proof that {@link add} will succeed, and the gap is
|
|
19810
|
+
* Still NOT a proof that {@link add} will succeed, and the remaining gap is only the
|
|
19624
19811
|
* genuinely transient: a lock another process holds for longer than the retry
|
|
19625
19812
|
* budget, or a CAS race, either of which a retry actually fixes. What it DOES
|
|
19626
19813
|
* cover is every deterministic refusal — no writable secret store, and an index
|
|
@@ -19654,11 +19841,11 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19654
19841
|
* delete, and no other store PROVED it still holds a copy.
|
|
19655
19842
|
*
|
|
19656
19843
|
* That is deliberately WEAKER than "proved gone everywhere", and saying the
|
|
19657
|
-
* stronger thing (
|
|
19844
|
+
* stronger thing ("PROVED gone") would hide a real residual:
|
|
19658
19845
|
* a non-home store vetoes only on `present`, and a LOCKED keychain answers
|
|
19659
19846
|
* `unknown` — so on the one state this store's model is built around, a stale copy behind a
|
|
19660
19847
|
* locked non-home store passes unvetoed. That is the chosen trade, not an
|
|
19661
|
-
* oversight: the alternative (veto on doubt)
|
|
19848
|
+
* oversight: the alternative (veto on doubt) would let an unrelated
|
|
19662
19849
|
* locked keychain block removal of file-store entries forever, with advice to
|
|
19663
19850
|
* unlock a store that never held the secret.
|
|
19664
19851
|
*
|
|
@@ -19719,7 +19906,7 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19719
19906
|
* freshly minted `keyId` on every path (`issueOrRotateKey` either creates a new key
|
|
19720
19907
|
* or revokes-and-recreates, and both mint a new id), so `prior.secretRef !==
|
|
19721
19908
|
* stamped.secretRef` always holds and `add` always takes the `forgetEverywhere`
|
|
19722
|
-
* branch.
|
|
19909
|
+
* branch. That does not make this dead code — it is UNEXERCISED by `join`, and
|
|
19723
19910
|
* the distinction matters:
|
|
19724
19911
|
*
|
|
19725
19912
|
* - `add` is public and takes the entry as an argument, so "the caller supplies a
|
|
@@ -19786,10 +19973,10 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19786
19973
|
* `keyId`, so dropping it while the `ssk_*` survives leaves a live, unrevokable
|
|
19787
19974
|
* credential on the machine.
|
|
19788
19975
|
*
|
|
19789
|
-
* The gate is the DELETE'S RESULT, not whether the store looked reachable.
|
|
19790
|
-
*
|
|
19976
|
+
* The gate is the DELETE'S RESULT, not whether the store looked reachable. A
|
|
19977
|
+
* reachability gate would not work, because a
|
|
19791
19978
|
* LOCKED keychain is perfectly reachable — it constructs, it answers, it just
|
|
19792
|
-
* won't do anything. The guard never
|
|
19979
|
+
* won't do anything. The guard would never fire, the delete would quietly fail, the row would be
|
|
19793
19980
|
* dropped. Reachability is a proxy; "is it gone?" is the question.
|
|
19794
19981
|
*
|
|
19795
19982
|
* The secret is destroyed BEFORE the index row, the opposite of {@link add}, and
|
|
@@ -19804,10 +19991,8 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19804
19991
|
* for as long as a user takes to type would time out every other vectros process,
|
|
19805
19992
|
* including an MCP server trying to start. The lock buys nothing here anyway —
|
|
19806
19993
|
* `add` writes its secret outside the lock too, so the two secret-writes were
|
|
19807
|
-
* never serialised by it. So: the INITIAL read is unlocked (
|
|
19808
|
-
*
|
|
19809
|
-
* under a lock" — false too: the commit-time re-check below IS a read inside
|
|
19810
|
-
* `updateKeyring`'s lock). Delete outside the lock, then commit under it,
|
|
19994
|
+
* never serialised by it. So: the INITIAL read is unlocked (the commit-time
|
|
19995
|
+
* re-check below, by contrast, IS a read inside `updateKeyring`'s lock). Delete outside the lock, then commit under it,
|
|
19811
19996
|
* re-checking that the entry is still the one the unlocked read saw. That locked
|
|
19812
19997
|
* re-check is what stands in for having read under the lock to begin with.
|
|
19813
19998
|
*/
|
|
@@ -19957,18 +20142,17 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19957
20142
|
* already HAS the secret, so every failure here is silent and leaves the machine
|
|
19958
20143
|
* as it found it, for the next read to retry.
|
|
19959
20144
|
*
|
|
19960
|
-
* "As it found it" is a promise about BOTH stores
|
|
19961
|
-
*
|
|
19962
|
-
*
|
|
20145
|
+
* "As it found it" is a promise about BOTH stores. Saying failure "simply leaves the
|
|
20146
|
+
* secret where it was" would be false for any abort path that returned after
|
|
20147
|
+
* `setSecret` had already landed a SECOND copy in
|
|
19963
20148
|
* the target and left it there — a copy of a live `ssk_*` that no index row names,
|
|
19964
20149
|
* i.e. the exact strand `forgetEverywhere` exists to prevent, produced by the
|
|
19965
20150
|
* function that migrates.
|
|
19966
20151
|
*
|
|
19967
20152
|
* So the target copy is treated as UNCOMMITTED until the migration actually lands,
|
|
19968
20153
|
* and `finally` clears it on every other route out. That is structural rather than
|
|
19969
|
-
* remembered:
|
|
19970
|
-
*
|
|
19971
|
-
* file a review round. The flag cannot be forgotten on a path added later.
|
|
20154
|
+
* remembered: a hand-written cleanup per abort path is precisely the shape that gets
|
|
20155
|
+
* forgotten on one of them. The flag cannot be forgotten on a path added later.
|
|
19972
20156
|
*
|
|
19973
20157
|
* The ordering is the rest of the design, and each step guards a different disaster:
|
|
19974
20158
|
* write the new copy; read it back (a write that silently didn't land would
|
|
@@ -21129,7 +21313,7 @@ async function resolveBridgeCognitoToken(args, prompt, isTty, resolveCognito = r
|
|
|
21129
21313
|
}
|
|
21130
21314
|
if (!isTty) {
|
|
21131
21315
|
throw new Error(
|
|
21132
|
-
|
|
21316
|
+
`No bridge token: set VECTROS_BOOTSTRAP_TOKEN, pass --token, or run \`${loginCommandFor(args.env)}\` first (required in non-interactive mode).`
|
|
21133
21317
|
);
|
|
21134
21318
|
}
|
|
21135
21319
|
process.stdout.write(
|
|
@@ -21238,7 +21422,9 @@ ${renderPlan(blueprint, planSteps)}
|
|
|
21238
21422
|
`);
|
|
21239
21423
|
const declaredIdentities = Object.entries(blueprint.identities ?? {});
|
|
21240
21424
|
if (declaredIdentities.length > 0) {
|
|
21241
|
-
process.stdout.write(
|
|
21425
|
+
process.stdout.write(`
|
|
21426
|
+
${IDENTITIES_TO_ENSURE_HEADING}
|
|
21427
|
+
`);
|
|
21242
21428
|
for (const [name, decl] of declaredIdentities) {
|
|
21243
21429
|
process.stdout.write(` ~ ${name} (${decl.kind}) \u2014 external id '${decl.externalId}'
|
|
21244
21430
|
`);
|
|
@@ -21469,6 +21655,12 @@ ${codeAddHint}`);
|
|
|
21469
21655
|
}
|
|
21470
21656
|
return finishApply();
|
|
21471
21657
|
} catch (err) {
|
|
21658
|
+
if (err instanceof SessionExpiredError) {
|
|
21659
|
+
process.stderr.write(`
|
|
21660
|
+
\u2716 ${err.message}
|
|
21661
|
+
`);
|
|
21662
|
+
return 2;
|
|
21663
|
+
}
|
|
21472
21664
|
if (err instanceof LostKeyError) {
|
|
21473
21665
|
process.stderr.write(`
|
|
21474
21666
|
\u2716 ${err.message}
|
|
@@ -21840,7 +22032,9 @@ ${renderPlan(blueprint, buildPlan(blueprint, { noSeed, noKey: true }), { prune:
|
|
|
21840
22032
|
process.stdout.write(applySeedNotice(blueprint, args.seed));
|
|
21841
22033
|
const declaredIdentities = Object.entries(blueprint.identities ?? {});
|
|
21842
22034
|
if (declaredIdentities.length > 0) {
|
|
21843
|
-
process.stdout.write(
|
|
22035
|
+
process.stdout.write(`
|
|
22036
|
+
${IDENTITIES_TO_ENSURE_HEADING}
|
|
22037
|
+
`);
|
|
21844
22038
|
for (const [name, decl] of declaredIdentities) {
|
|
21845
22039
|
process.stdout.write(` ~ ${name} (${decl.kind}) \u2014 external id '${decl.externalId}'
|
|
21846
22040
|
`);
|
|
@@ -21973,7 +22167,7 @@ function reportApplyError(err) {
|
|
|
21973
22167
|
If this is expected (a re-apply of a blueprint you manage), re-run with --confirm-existing-principal
|
|
21974
22168
|
(with --yes in a pipeline, where it is standing consent), or confirm at the prompt.
|
|
21975
22169
|
If you don't recognize this identity, investigate the blueprint's servicePrincipal.externalId first.
|
|
21976
|
-
Only the
|
|
22170
|
+
Only the principal lookup had run; no context, grant or anything else was created or changed.
|
|
21977
22171
|
`
|
|
21978
22172
|
);
|
|
21979
22173
|
return 3;
|
|
@@ -22326,10 +22520,14 @@ schemas:
|
|
|
22326
22520
|
accessProfile:
|
|
22327
22521
|
# SCOPE GATE: data-plane only (records/schemas/search/documents/folders/inference/entities),
|
|
22328
22522
|
# PLUS profiles:r/u/d (bounded by the platform to redistributing access you already have,
|
|
22329
|
-
# never granting more)
|
|
22330
|
-
# platform resolves "self" from the caller, not from anything you declare here)
|
|
22331
|
-
#
|
|
22332
|
-
#
|
|
22523
|
+
# never granting more), profiles:c:self (create a profile for yourself only \u2014 the
|
|
22524
|
+
# platform resolves "self" from the caller, not from anything you declare here) and
|
|
22525
|
+
# scripts:x:<script-name> (run one named script that this blueprint ships; there is no bare
|
|
22526
|
+
# or wildcard form).
|
|
22527
|
+
# A bare profiles:c is accepted ONLY when this same clause also declares the
|
|
22528
|
+
# member-lifecycle capability (see "capabilities" below); without it, and always for
|
|
22529
|
+
# profiles:c:usr_<id> naming another principal, it is REJECTED \u2014 no override. Every other
|
|
22530
|
+
# control-plane resource (keys/users/admin/namespaces/triggers/...) and "*" is REJECTED the same way.
|
|
22333
22531
|
# Ops: c|r|u|d, combinable (records:cru). Prefer the narrowest set (least privilege).
|
|
22334
22532
|
allowedActions: [records:r, records:c, records:u, search:r, schemas:r]
|
|
22335
22533
|
# dataScope binds the key to specific owners. Include null to ALSO see
|
|
@@ -22345,8 +22543,10 @@ accessProfile:
|
|
|
22345
22543
|
# above) unless every owner genuinely sharing every record is what you want.
|
|
22346
22544
|
dataScope: {}
|
|
22347
22545
|
# capabilities is a SEPARATE gated dimension from allowedActions \u2014 named platform
|
|
22348
|
-
# effects a resource:ops verb can't express. Only 'member-lifecycle'
|
|
22349
|
-
# today; every other name is
|
|
22546
|
+
# effects a resource:ops verb can't express. Only 'member-lifecycle' and
|
|
22547
|
+
# 'delegate-principal-stamp' are admitted today; every other name is REJECTED, same
|
|
22548
|
+
# posture as the scope gate above. 'member-lifecycle' is also what lets this clause
|
|
22549
|
+
# carry a bare profiles:c.
|
|
22350
22550
|
# capabilities: [member-lifecycle]
|
|
22351
22551
|
|
|
22352
22552
|
servicePrincipal:
|
|
@@ -22433,8 +22633,10 @@ Usage:
|
|
|
22433
22633
|
|
|
22434
22634
|
<file|name> is a path to your own blueprint (YAML .yaml/.yml or JSON .json) OR
|
|
22435
22635
|
the name of a bundled blueprint. The same scope gate that protects bundled
|
|
22436
|
-
blueprints applies to file blueprints
|
|
22437
|
-
|
|
22636
|
+
blueprints applies to file blueprints: beyond data-plane access, a forked or
|
|
22637
|
+
hand-written blueprint can request only a short allowlist of narrow exceptions
|
|
22638
|
+
(profiles:r/u/d, profiles:c:self, a bare profiles:c beside member-lifecycle,
|
|
22639
|
+
scripts:x:<script-name>); every other control-plane scope is rejected.
|
|
22438
22640
|
|
|
22439
22641
|
A blueprint may declare a top-level 'inputs:' block and reference its values
|
|
22440
22642
|
with \${{ inputs.<name> }} (and the built-ins \${{ vectros.context }} /
|
|
@@ -22546,7 +22748,9 @@ ${renderPlan(blueprint, steps)}
|
|
|
22546
22748
|
`);
|
|
22547
22749
|
const declaredIdentities = Object.entries(blueprint.identities ?? {});
|
|
22548
22750
|
if (declaredIdentities.length > 0) {
|
|
22549
|
-
process.stdout.write(
|
|
22751
|
+
process.stdout.write(`
|
|
22752
|
+
${IDENTITIES_TO_ENSURE_HEADING}
|
|
22753
|
+
`);
|
|
22550
22754
|
for (const [name, decl] of declaredIdentities) {
|
|
22551
22755
|
process.stdout.write(` ~ ${name} (${decl.kind}) \u2014 external id '${decl.externalId}'
|
|
22552
22756
|
`);
|
|
@@ -22680,6 +22884,89 @@ ${BLUEPRINT_HELP}
|
|
|
22680
22884
|
// src/program.ts
|
|
22681
22885
|
var import_commander = require("commander");
|
|
22682
22886
|
|
|
22887
|
+
// src/dev-api.ts
|
|
22888
|
+
var DeveloperApiError = class extends Error {
|
|
22889
|
+
statusCode;
|
|
22890
|
+
/** The platform's error body, when it answered with one (its message, request id and error code). */
|
|
22891
|
+
body;
|
|
22892
|
+
constructor(message, statusCode, body) {
|
|
22893
|
+
super(message);
|
|
22894
|
+
this.name = "DeveloperApiError";
|
|
22895
|
+
this.statusCode = statusCode;
|
|
22896
|
+
this.body = body;
|
|
22897
|
+
}
|
|
22898
|
+
};
|
|
22899
|
+
async function fetchDeveloperUsersPage(opts) {
|
|
22900
|
+
const { cognitoToken, apiBaseUrl, tenant, startFrom, limit, fetchImpl = fetch } = opts;
|
|
22901
|
+
const base = apiBaseUrl.replace(/\/$/, "");
|
|
22902
|
+
let url = `${base}/developer/users?tenant=${encodeURIComponent(tenant)}`;
|
|
22903
|
+
if (startFrom) url += `&startFrom=${encodeURIComponent(startFrom)}`;
|
|
22904
|
+
if (limit !== void 0) url += `&limit=${encodeURIComponent(String(limit))}`;
|
|
22905
|
+
let res;
|
|
22906
|
+
try {
|
|
22907
|
+
res = await fetchImpl(url, {
|
|
22908
|
+
method: "GET",
|
|
22909
|
+
headers: { Authorization: `Bearer ${cognitoToken}` }
|
|
22910
|
+
});
|
|
22911
|
+
} catch (err) {
|
|
22912
|
+
throw new DeveloperApiError(
|
|
22913
|
+
`Request to /developer/users failed: ${err instanceof Error ? err.message : String(err)}`
|
|
22914
|
+
);
|
|
22915
|
+
}
|
|
22916
|
+
if (!res.ok) {
|
|
22917
|
+
const body = await res.text().catch(() => "");
|
|
22918
|
+
const hint = res.status === 403 ? " (this command requires an account owner login \u2014 sign in as the owner and retry)" : "";
|
|
22919
|
+
throw new DeveloperApiError(
|
|
22920
|
+
`GET /developer/users failed: HTTP ${res.status}${hint}${body ? ` \u2014 ${body}` : ""}`,
|
|
22921
|
+
res.status
|
|
22922
|
+
);
|
|
22923
|
+
}
|
|
22924
|
+
let parsed;
|
|
22925
|
+
try {
|
|
22926
|
+
parsed = await res.json();
|
|
22927
|
+
} catch {
|
|
22928
|
+
throw new DeveloperApiError("GET /developer/users returned a non-JSON body");
|
|
22929
|
+
}
|
|
22930
|
+
const data = Array.isArray(parsed.data) ? parsed.data : [];
|
|
22931
|
+
const nextCursor = typeof parsed.nextCursor === "string" ? parsed.nextCursor : null;
|
|
22932
|
+
return { data, nextCursor };
|
|
22933
|
+
}
|
|
22934
|
+
async function fetchDeveloperMe(opts) {
|
|
22935
|
+
const { cognitoToken, apiBaseUrl, tenant, fetchImpl = fetch } = opts;
|
|
22936
|
+
const url = `${apiBaseUrl.replace(/\/$/, "")}/developer/me?tenant=${encodeURIComponent(tenant)}`;
|
|
22937
|
+
let res;
|
|
22938
|
+
try {
|
|
22939
|
+
res = await fetchImpl(url, { method: "GET", headers: { Authorization: `Bearer ${cognitoToken}` } });
|
|
22940
|
+
} catch (err) {
|
|
22941
|
+
throw new DeveloperApiError(
|
|
22942
|
+
`Request to /developer/me failed: ${err instanceof Error ? err.message : String(err)}`
|
|
22943
|
+
);
|
|
22944
|
+
}
|
|
22945
|
+
if (res.status === 404) return null;
|
|
22946
|
+
if (!res.ok) {
|
|
22947
|
+
const failure = await devRouteError(res, "GET /developer/me");
|
|
22948
|
+
throw new DeveloperApiError(
|
|
22949
|
+
`GET /developer/me failed: HTTP ${res.status}`,
|
|
22950
|
+
res.status,
|
|
22951
|
+
failure.body
|
|
22952
|
+
);
|
|
22953
|
+
}
|
|
22954
|
+
let parsed;
|
|
22955
|
+
try {
|
|
22956
|
+
parsed = await res.json();
|
|
22957
|
+
} catch {
|
|
22958
|
+
throw new DeveloperApiError("GET /developer/me returned a non-JSON body");
|
|
22959
|
+
}
|
|
22960
|
+
if (typeof parsed.principalId !== "string" || typeof parsed.userId !== "string") {
|
|
22961
|
+
throw new DeveloperApiError("GET /developer/me returned no principal");
|
|
22962
|
+
}
|
|
22963
|
+
return {
|
|
22964
|
+
principalId: parsed.principalId,
|
|
22965
|
+
userId: parsed.userId,
|
|
22966
|
+
tenantId: typeof parsed.tenantId === "string" ? parsed.tenantId : void 0
|
|
22967
|
+
};
|
|
22968
|
+
}
|
|
22969
|
+
|
|
22683
22970
|
// src/self-principal.ts
|
|
22684
22971
|
var PRINCIPAL_PREFIX_USER = "usr_";
|
|
22685
22972
|
function emailFromJwt(token) {
|
|
@@ -22708,6 +22995,37 @@ async function resolveSelfPrincipal(client, cognitoToken) {
|
|
|
22708
22995
|
if (!match?.id) return null;
|
|
22709
22996
|
return { principalId: `${PRINCIPAL_PREFIX_USER}${match.id}`, userId: match.id, email };
|
|
22710
22997
|
}
|
|
22998
|
+
async function resolveSelfPrincipalForTenant(auth, fetchImpl) {
|
|
22999
|
+
const tenant = auth.tenant ?? "live";
|
|
23000
|
+
let me;
|
|
23001
|
+
try {
|
|
23002
|
+
me = await fetchDeveloperMe({
|
|
23003
|
+
cognitoToken: auth.cognitoToken,
|
|
23004
|
+
apiBaseUrl: auth.baseUrl,
|
|
23005
|
+
tenant,
|
|
23006
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
23007
|
+
});
|
|
23008
|
+
} catch (err) {
|
|
23009
|
+
if (err instanceof DeveloperApiError && err.statusCode !== void 0) {
|
|
23010
|
+
if (err.body?.errorCode === "SESSION_REFRESH_REQUIRED") throw new SessionExpiredError(auth.env ?? "production");
|
|
23011
|
+
throw err;
|
|
23012
|
+
}
|
|
23013
|
+
if (tenant === "test") throw err;
|
|
23014
|
+
return resolveSelfPrincipal(auth.client, auth.cognitoToken);
|
|
23015
|
+
}
|
|
23016
|
+
if (me === null) return null;
|
|
23017
|
+
const forThisTenant = Boolean(auth.tenantId && me.tenantId && me.tenantId === auth.tenantId);
|
|
23018
|
+
if (forThisTenant && me.principalId.startsWith(PRINCIPAL_PREFIX_USER)) {
|
|
23019
|
+
return { principalId: me.principalId, userId: me.userId, email: emailFromJwt(auth.cognitoToken) ?? "" };
|
|
23020
|
+
}
|
|
23021
|
+
return tenant === "live" ? resolveSelfPrincipal(auth.client, auth.cognitoToken) : null;
|
|
23022
|
+
}
|
|
23023
|
+
function selfPrincipalUnresolvedMessage(tenant, flag) {
|
|
23024
|
+
if (tenant === "test") {
|
|
23025
|
+
return `Could not resolve your own user id in the test tenant: the platform did not report one for this login. Find it with \`vectros identity list-all --tenant test\` (an owner login) and pass ${flag} usr_<id> explicitly.`;
|
|
23026
|
+
}
|
|
23027
|
+
return `Could not resolve your own user id from your login. Pass ${flag} usr_<id> explicitly (find the id with \`vectros identity list --type user\`).`;
|
|
23028
|
+
}
|
|
22711
23029
|
|
|
22712
23030
|
// src/commands/whoami.ts
|
|
22713
23031
|
async function runWhoami(options, deps = {}) {
|
|
@@ -22715,12 +23033,19 @@ async function runWhoami(options, deps = {}) {
|
|
|
22715
23033
|
const { identity, baseUrl } = auth;
|
|
22716
23034
|
let self2 = null;
|
|
22717
23035
|
try {
|
|
22718
|
-
self2 = await
|
|
23036
|
+
self2 = await resolveSelfPrincipalForTenant(
|
|
23037
|
+
{ ...auth, client: auth.client },
|
|
23038
|
+
deps.fetchImpl
|
|
23039
|
+
);
|
|
22719
23040
|
} catch {
|
|
22720
23041
|
self2 = null;
|
|
22721
23042
|
}
|
|
22722
23043
|
if (options.json) {
|
|
22723
|
-
const out =
|
|
23044
|
+
const out = {
|
|
23045
|
+
...identity,
|
|
23046
|
+
tenantType: auth.tenant,
|
|
23047
|
+
...self2 ? { userId: self2.userId, userPrincipalId: self2.principalId } : {}
|
|
23048
|
+
};
|
|
22724
23049
|
process.stdout.write(`${formatJson(out)}
|
|
22725
23050
|
`);
|
|
22726
23051
|
return 0;
|
|
@@ -22729,6 +23054,7 @@ async function runWhoami(options, deps = {}) {
|
|
|
22729
23054
|
process.stdout.write(
|
|
22730
23055
|
"\n" + formatKeyValues([
|
|
22731
23056
|
["tenant", identity.tenantId],
|
|
23057
|
+
["tenant type", auth.tenant],
|
|
22732
23058
|
["environment", identity.environment],
|
|
22733
23059
|
["principal", identity.principalType],
|
|
22734
23060
|
["principal id", identity.principalKeyId],
|
|
@@ -23121,51 +23447,6 @@ ${list}
|
|
|
23121
23447
|
});
|
|
23122
23448
|
}
|
|
23123
23449
|
|
|
23124
|
-
// src/dev-api.ts
|
|
23125
|
-
var DeveloperApiError = class extends Error {
|
|
23126
|
-
statusCode;
|
|
23127
|
-
constructor(message, statusCode) {
|
|
23128
|
-
super(message);
|
|
23129
|
-
this.name = "DeveloperApiError";
|
|
23130
|
-
this.statusCode = statusCode;
|
|
23131
|
-
}
|
|
23132
|
-
};
|
|
23133
|
-
async function fetchDeveloperUsersPage(opts) {
|
|
23134
|
-
const { cognitoToken, apiBaseUrl, tenant, startFrom, limit, fetchImpl = fetch } = opts;
|
|
23135
|
-
const base = apiBaseUrl.replace(/\/$/, "");
|
|
23136
|
-
let url = `${base}/developer/users?tenant=${encodeURIComponent(tenant)}`;
|
|
23137
|
-
if (startFrom) url += `&startFrom=${encodeURIComponent(startFrom)}`;
|
|
23138
|
-
if (limit !== void 0) url += `&limit=${encodeURIComponent(String(limit))}`;
|
|
23139
|
-
let res;
|
|
23140
|
-
try {
|
|
23141
|
-
res = await fetchImpl(url, {
|
|
23142
|
-
method: "GET",
|
|
23143
|
-
headers: { Authorization: `Bearer ${cognitoToken}` }
|
|
23144
|
-
});
|
|
23145
|
-
} catch (err) {
|
|
23146
|
-
throw new DeveloperApiError(
|
|
23147
|
-
`Request to /developer/users failed: ${err instanceof Error ? err.message : String(err)}`
|
|
23148
|
-
);
|
|
23149
|
-
}
|
|
23150
|
-
if (!res.ok) {
|
|
23151
|
-
const body = await res.text().catch(() => "");
|
|
23152
|
-
const hint = res.status === 403 ? " (this command requires an account owner login \u2014 sign in as the owner and retry)" : "";
|
|
23153
|
-
throw new DeveloperApiError(
|
|
23154
|
-
`GET /developer/users failed: HTTP ${res.status}${hint}${body ? ` \u2014 ${body}` : ""}`,
|
|
23155
|
-
res.status
|
|
23156
|
-
);
|
|
23157
|
-
}
|
|
23158
|
-
let parsed;
|
|
23159
|
-
try {
|
|
23160
|
-
parsed = await res.json();
|
|
23161
|
-
} catch {
|
|
23162
|
-
throw new DeveloperApiError("GET /developer/users returned a non-JSON body");
|
|
23163
|
-
}
|
|
23164
|
-
const data = Array.isArray(parsed.data) ? parsed.data : [];
|
|
23165
|
-
const nextCursor = typeof parsed.nextCursor === "string" ? parsed.nextCursor : null;
|
|
23166
|
-
return { data, nextCursor };
|
|
23167
|
-
}
|
|
23168
|
-
|
|
23169
23450
|
// src/commands/identity.ts
|
|
23170
23451
|
function isUserDimension(type) {
|
|
23171
23452
|
return type === "user";
|
|
@@ -23461,14 +23742,13 @@ function assertPrincipalShape(principal) {
|
|
|
23461
23742
|
}
|
|
23462
23743
|
}
|
|
23463
23744
|
var SELF_PRINCIPAL_ALIAS = "me";
|
|
23464
|
-
async function resolvePrincipalArg(raw, auth) {
|
|
23745
|
+
async function resolvePrincipalArg(raw, auth, deps) {
|
|
23465
23746
|
if (raw !== SELF_PRINCIPAL_ALIAS) return raw;
|
|
23466
|
-
const self2 = await
|
|
23467
|
-
|
|
23468
|
-
|
|
23469
|
-
|
|
23470
|
-
|
|
23471
|
-
}
|
|
23747
|
+
const self2 = await resolveSelfPrincipalForTenant(
|
|
23748
|
+
{ ...auth, client: auth.client },
|
|
23749
|
+
deps.fetchImpl
|
|
23750
|
+
);
|
|
23751
|
+
if (!self2) throw new Error(selfPrincipalUnresolvedMessage(auth.tenant, "--principal"));
|
|
23472
23752
|
return self2.principalId;
|
|
23473
23753
|
}
|
|
23474
23754
|
function roleSummary(p) {
|
|
@@ -23521,7 +23801,7 @@ async function runAccessGrant(opts, deps = {}) {
|
|
|
23521
23801
|
return withAuth(opts, deps, async (auth) => {
|
|
23522
23802
|
const client = auth.client;
|
|
23523
23803
|
try {
|
|
23524
|
-
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
23804
|
+
const principal = await resolvePrincipalArg(opts.principal, auth, deps);
|
|
23525
23805
|
const scopes = hasActions ? [{ allowed_actions: splitCsv(opts.actions) }] : void 0;
|
|
23526
23806
|
const profile = await client.auth.createAccessProfile({
|
|
23527
23807
|
contextId: opts.context,
|
|
@@ -23531,8 +23811,10 @@ async function runAccessGrant(opts, deps = {}) {
|
|
|
23531
23811
|
// Always send the plural `roleIds`, even for a single
|
|
23532
23812
|
// `--role`: it is a strict superset of the deprecated singular
|
|
23533
23813
|
// `roleId` (equivalent to `roleIds: ["<value>"]`), and the platform
|
|
23534
|
-
// reads it uniformly regardless of how many roles compose.
|
|
23535
|
-
|
|
23814
|
+
// reads it uniformly regardless of how many roles compose. With
|
|
23815
|
+
// `--actions` there is no role: omit `roleIds` (the platform refuses an
|
|
23816
|
+
// empty list next to inline `scopes`).
|
|
23817
|
+
roleIds: hasRole ? opts.role : void 0,
|
|
23536
23818
|
scopes,
|
|
23537
23819
|
identityOverrides
|
|
23538
23820
|
}
|
|
@@ -23569,7 +23851,7 @@ async function runAccessRevoke(opts, deps = {}) {
|
|
|
23569
23851
|
return withAuth(opts, deps, async (auth) => {
|
|
23570
23852
|
const client = auth.client;
|
|
23571
23853
|
try {
|
|
23572
|
-
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
23854
|
+
const principal = await resolvePrincipalArg(opts.principal, auth, deps);
|
|
23573
23855
|
await client.auth.deleteAccessProfile({ contextId: opts.context, principalId: principal });
|
|
23574
23856
|
process.stdout.write(` \u2714 Revoked ${principal} from '${opts.context}'
|
|
23575
23857
|
`);
|
|
@@ -23581,6 +23863,12 @@ async function runAccessRevoke(opts, deps = {}) {
|
|
|
23581
23863
|
}
|
|
23582
23864
|
});
|
|
23583
23865
|
}
|
|
23866
|
+
function searchedContextNotice(principal, rows, tenant) {
|
|
23867
|
+
if (rows.some((r) => r.contextId !== void 0 && r.contextId !== DEFAULT_CONTEXT_ID)) return void 0;
|
|
23868
|
+
const tenantFlag = tenant === "test" ? " --tenant test" : "";
|
|
23869
|
+
return ` \u2139 Listed with a token pinned to context '${DEFAULT_CONTEXT_ID}' (${tenant} tenant). Depending on the caller, the platform may not look beyond that context, so a grant in another context may be missing; an empty result means none in '${DEFAULT_CONTEXT_ID}'. Check a context with: vectros access get --principal ${principal} --context <c>${tenantFlag}
|
|
23870
|
+
`;
|
|
23871
|
+
}
|
|
23584
23872
|
async function runAccessList(opts, deps = {}) {
|
|
23585
23873
|
if (Boolean(opts.context) === Boolean(opts.principal)) {
|
|
23586
23874
|
process.stderr.write(" \u2716 Provide exactly one of --context <c> or --principal <p>.\n");
|
|
@@ -23598,15 +23886,17 @@ async function runAccessList(opts, deps = {}) {
|
|
|
23598
23886
|
return withAuth(opts, deps, async (auth) => {
|
|
23599
23887
|
const client = auth.client;
|
|
23600
23888
|
try {
|
|
23601
|
-
const principal = opts.principal ? await resolvePrincipalArg(opts.principal, auth) : void 0;
|
|
23889
|
+
const principal = opts.principal ? await resolvePrincipalArg(opts.principal, auth, deps) : void 0;
|
|
23602
23890
|
const rows = opts.context ? await drainPages(
|
|
23603
23891
|
(startFrom) => client.auth.listAccessProfiles({ contextId: opts.context, startFrom, limit: PAGE_LIMIT })
|
|
23604
23892
|
) : await drainPages(
|
|
23605
23893
|
(startFrom) => client.auth.listProfilesForPrincipal({ principalId: principal, startFrom, limit: PAGE_LIMIT })
|
|
23606
23894
|
);
|
|
23895
|
+
const notice = opts.principal ? searchedContextNotice(opts.principal, rows, auth.tenant) : void 0;
|
|
23607
23896
|
if (opts.json) {
|
|
23608
23897
|
process.stdout.write(`${formatJson(rows)}
|
|
23609
23898
|
`);
|
|
23899
|
+
if (notice) process.stderr.write(notice);
|
|
23610
23900
|
return 0;
|
|
23611
23901
|
}
|
|
23612
23902
|
process.stdout.write(
|
|
@@ -23617,6 +23907,7 @@ async function runAccessList(opts, deps = {}) {
|
|
|
23617
23907
|
{ header: "status", get: (r) => r.status }
|
|
23618
23908
|
]) + "\n"
|
|
23619
23909
|
);
|
|
23910
|
+
if (notice) process.stderr.write(notice);
|
|
23620
23911
|
return 0;
|
|
23621
23912
|
} catch (err) {
|
|
23622
23913
|
process.stderr.write(` \u2716 access list failed: ${formatError(err)}
|
|
@@ -23638,7 +23929,7 @@ async function runAccessGet(opts, deps = {}) {
|
|
|
23638
23929
|
return withAuth(opts, deps, async (auth) => {
|
|
23639
23930
|
const client = auth.client;
|
|
23640
23931
|
try {
|
|
23641
|
-
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
23932
|
+
const principal = await resolvePrincipalArg(opts.principal, auth, deps);
|
|
23642
23933
|
const p = await client.auth.getAccessProfile({ contextId: opts.context, principalId: principal });
|
|
23643
23934
|
if (opts.json) {
|
|
23644
23935
|
process.stdout.write(`${formatJson(p)}
|
|
@@ -23860,11 +24151,13 @@ async function runAccessExplain(opts, deps = {}) {
|
|
|
23860
24151
|
try {
|
|
23861
24152
|
let principal = opts.principal;
|
|
23862
24153
|
if (principal === SELF_PRINCIPAL_ALIAS2) {
|
|
23863
|
-
const self2 = await
|
|
24154
|
+
const self2 = await resolveSelfPrincipalForTenant(
|
|
24155
|
+
{ ...auth, client: auth.client },
|
|
24156
|
+
deps.fetchImpl
|
|
24157
|
+
);
|
|
23864
24158
|
if (!self2) {
|
|
23865
|
-
process.stderr.write(
|
|
23866
|
-
|
|
23867
|
-
);
|
|
24159
|
+
process.stderr.write(` \u2716 ${selfPrincipalUnresolvedMessage(auth.tenant, "--principal")}
|
|
24160
|
+
`);
|
|
23868
24161
|
return 1;
|
|
23869
24162
|
}
|
|
23870
24163
|
principal = self2.principalId;
|
|
@@ -24243,10 +24536,17 @@ async function runContextDestroy(opts, deps = {}) {
|
|
|
24243
24536
|
`);
|
|
24244
24537
|
return 0;
|
|
24245
24538
|
}
|
|
24539
|
+
const tenantFlag = auth.tenant === "test" ? " --tenant test" : "";
|
|
24246
24540
|
process.stdout.write(
|
|
24247
24541
|
`
|
|
24248
24542
|
\u2714 teardown of '${contextId}' started \u2014 the context and its contents are being
|
|
24249
24543
|
deleted asynchronously (it reports status 'purging' until the drain completes)
|
|
24544
|
+
|
|
24545
|
+
Not removed: a service principal a blueprint bootstrap created for this context (the
|
|
24546
|
+
externalId in the blueprint's servicePrincipal) belongs to the whole tenant, so it stays. To clean it up
|
|
24547
|
+
when it exists and nothing else uses it:
|
|
24548
|
+
vectros identity list-all${tenantFlag}
|
|
24549
|
+
vectros identity delete --type user --id <id>${tenantFlag}
|
|
24250
24550
|
`
|
|
24251
24551
|
);
|
|
24252
24552
|
return 0;
|
|
@@ -24785,7 +25085,7 @@ ${renderIssuer(issuer)}
|
|
|
24785
25085
|
var import_node_fs5 = require("fs");
|
|
24786
25086
|
var readline6 = __toESM(require("readline/promises"));
|
|
24787
25087
|
var PROVIDER_TYPES = ["ANTHROPIC_NATIVE", "OPENAI_COMPATIBLE"];
|
|
24788
|
-
var BILLING_MODES = ["
|
|
25088
|
+
var BILLING_MODES = ["CUSTOMER_DIRECT"];
|
|
24789
25089
|
var STATUSES = ["ACTIVE", "DISABLED"];
|
|
24790
25090
|
var MAX_SECRET_BYTES = 8 * 1024;
|
|
24791
25091
|
async function readBoundedSecret(stream) {
|
|
@@ -24864,12 +25164,17 @@ function renderProviderConfig(c) {
|
|
|
24864
25164
|
["defaultProviderModelId", c.defaultProviderModelId],
|
|
24865
25165
|
["waiverType", c.waiverType],
|
|
24866
25166
|
["billingMode", c.billingMode],
|
|
24867
|
-
["platformTokenFactor", c.platformTokenFactor],
|
|
24868
|
-
["platformOutputWeight", c.platformOutputWeight],
|
|
24869
25167
|
["status", c.status],
|
|
24870
25168
|
["createdAt", c.createdAt]
|
|
24871
25169
|
]);
|
|
24872
25170
|
}
|
|
25171
|
+
function billingModeError(value) {
|
|
25172
|
+
if (value === void 0 || BILLING_MODES.includes(value)) return void 0;
|
|
25173
|
+
if (value === "PLATFORM_BILLED") {
|
|
25174
|
+
return "--billing-mode PLATFORM_BILLED is retired: a provider you configure bills you directly, so CUSTOMER_DIRECT is the only billing mode.";
|
|
25175
|
+
}
|
|
25176
|
+
return invalidEnum("--billing-mode", value, BILLING_MODES);
|
|
25177
|
+
}
|
|
24873
25178
|
function invalidEnum(flag, value, allowed) {
|
|
24874
25179
|
return `${flag} must be one of ${allowed.join(", ")} (got '${value}').`;
|
|
24875
25180
|
}
|
|
@@ -24880,8 +25185,9 @@ async function runProviderConfigCreate(opts, deps = {}) {
|
|
|
24880
25185
|
`);
|
|
24881
25186
|
return 2;
|
|
24882
25187
|
}
|
|
24883
|
-
|
|
24884
|
-
|
|
25188
|
+
const billingModeProblem = billingModeError(opts.billingMode);
|
|
25189
|
+
if (billingModeProblem !== void 0) {
|
|
25190
|
+
process.stderr.write(` \u2716 ${billingModeProblem}
|
|
24885
25191
|
`);
|
|
24886
25192
|
return 2;
|
|
24887
25193
|
}
|
|
@@ -24890,19 +25196,6 @@ async function runProviderConfigCreate(opts, deps = {}) {
|
|
|
24890
25196
|
`);
|
|
24891
25197
|
return 2;
|
|
24892
25198
|
}
|
|
24893
|
-
if (opts.billingMode === "PLATFORM_BILLED") {
|
|
24894
|
-
if (opts.platformTokenFactor === void 0 || opts.platformOutputWeight === void 0) {
|
|
24895
|
-
process.stderr.write(
|
|
24896
|
-
" \u2716 --platform-token-factor and --platform-output-weight are required when --billing-mode PLATFORM_BILLED.\n"
|
|
24897
|
-
);
|
|
24898
|
-
return 2;
|
|
24899
|
-
}
|
|
24900
|
-
} else if (opts.platformTokenFactor !== void 0 || opts.platformOutputWeight !== void 0) {
|
|
24901
|
-
process.stderr.write(
|
|
24902
|
-
" \u2716 --platform-token-factor/--platform-output-weight only apply with --billing-mode PLATFORM_BILLED.\n"
|
|
24903
|
-
);
|
|
24904
|
-
return 2;
|
|
24905
|
-
}
|
|
24906
25199
|
let apiKeySecret;
|
|
24907
25200
|
try {
|
|
24908
25201
|
apiKeySecret = await resolveApiKeySecret(opts.apiKeySecretFile, deps, "required");
|
|
@@ -24927,9 +25220,7 @@ async function runProviderConfigCreate(opts, deps = {}) {
|
|
|
24927
25220
|
defaultProviderModelId: opts.defaultProviderModelId,
|
|
24928
25221
|
apiKeySecret,
|
|
24929
25222
|
waiverType: opts.waiverType,
|
|
24930
|
-
billingMode: opts.billingMode,
|
|
24931
|
-
platformTokenFactor: opts.platformTokenFactor,
|
|
24932
|
-
platformOutputWeight: opts.platformOutputWeight,
|
|
25223
|
+
billingMode: opts.billingMode ?? "CUSTOMER_DIRECT",
|
|
24933
25224
|
status: opts.status
|
|
24934
25225
|
})
|
|
24935
25226
|
});
|
|
@@ -25010,8 +25301,9 @@ async function runProviderConfigUpdate(opts, deps = {}) {
|
|
|
25010
25301
|
`);
|
|
25011
25302
|
return 2;
|
|
25012
25303
|
}
|
|
25013
|
-
|
|
25014
|
-
|
|
25304
|
+
const billingModeProblem = billingModeError(opts.billingMode);
|
|
25305
|
+
if (billingModeProblem !== void 0) {
|
|
25306
|
+
process.stderr.write(` \u2716 ${billingModeProblem}
|
|
25015
25307
|
`);
|
|
25016
25308
|
return 2;
|
|
25017
25309
|
}
|
|
@@ -25044,8 +25336,6 @@ async function runProviderConfigUpdate(opts, deps = {}) {
|
|
|
25044
25336
|
apiKeySecret,
|
|
25045
25337
|
waiverType: opts.waiverType,
|
|
25046
25338
|
billingMode: opts.billingMode,
|
|
25047
|
-
platformTokenFactor: opts.platformTokenFactor,
|
|
25048
|
-
platformOutputWeight: opts.platformOutputWeight,
|
|
25049
25339
|
status: opts.status
|
|
25050
25340
|
})
|
|
25051
25341
|
});
|
|
@@ -26622,15 +26912,14 @@ async function runBlueprintTest(fileOrName, opts, deps = {}) {
|
|
|
26622
26912
|
|
|
26623
26913
|
// src/commands/join.ts
|
|
26624
26914
|
var SELF_ALIAS = "me";
|
|
26625
|
-
async function resolvePrincipal(as, auth) {
|
|
26915
|
+
async function resolvePrincipal(as, auth, deps) {
|
|
26626
26916
|
const raw = as ?? SELF_ALIAS;
|
|
26627
26917
|
if (raw === SELF_ALIAS) {
|
|
26628
|
-
const self2 = await
|
|
26629
|
-
|
|
26630
|
-
|
|
26631
|
-
|
|
26632
|
-
|
|
26633
|
-
}
|
|
26918
|
+
const self2 = await resolveSelfPrincipalForTenant(
|
|
26919
|
+
{ ...auth, client: auth.client },
|
|
26920
|
+
deps.fetchImpl
|
|
26921
|
+
);
|
|
26922
|
+
if (!self2) throw new Error(selfPrincipalUnresolvedMessage(auth.tenant, "--as"));
|
|
26634
26923
|
return self2.principalId;
|
|
26635
26924
|
}
|
|
26636
26925
|
if (!raw.startsWith("usr_")) {
|
|
@@ -26700,7 +26989,7 @@ async function runJoin(opts, deps = {}) {
|
|
|
26700
26989
|
const client = auth.client;
|
|
26701
26990
|
try {
|
|
26702
26991
|
const tenantId = requireTenantId(auth);
|
|
26703
|
-
const principalId = await resolvePrincipal(opts.as, auth);
|
|
26992
|
+
const principalId = await resolvePrincipal(opts.as, auth, deps);
|
|
26704
26993
|
const userId = normalizePrincipalUserId(principalId);
|
|
26705
26994
|
const scopes = hasActions ? [{ allowed_actions: splitCsv(opts.actions) }] : void 0;
|
|
26706
26995
|
await client.auth.createAccessProfile({
|
|
@@ -26711,7 +27000,9 @@ async function runJoin(opts, deps = {}) {
|
|
|
26711
27000
|
// Always send the plural `roleIds`, even for a single
|
|
26712
27001
|
// `--role`: a strict superset of the deprecated singular `roleId`,
|
|
26713
27002
|
// read uniformly by the platform regardless of how many compose.
|
|
26714
|
-
|
|
27003
|
+
// With `--actions` there is no role: omit `roleIds` (the platform
|
|
27004
|
+
// refuses an empty list next to inline `scopes`).
|
|
27005
|
+
roleIds: hasRole ? opts.role : void 0,
|
|
26715
27006
|
scopes,
|
|
26716
27007
|
identityOverrides
|
|
26717
27008
|
}
|
|
@@ -26798,9 +27089,9 @@ function emitJoined(opts) {
|
|
|
26798
27089
|
|
|
26799
27090
|
// src/build-info.ts
|
|
26800
27091
|
var BUILD_INFO = {
|
|
26801
|
-
cli: true ? "0.
|
|
26802
|
-
sdk: true ? "0.
|
|
26803
|
-
blueprints: true ? "0.20.
|
|
27092
|
+
cli: true ? "0.26.0" : "dev",
|
|
27093
|
+
sdk: true ? "0.48.0" : "dev",
|
|
27094
|
+
blueprints: true ? "0.20.3" : "dev"
|
|
26804
27095
|
};
|
|
26805
27096
|
function formatBuildInfo() {
|
|
26806
27097
|
return `vectros ${BUILD_INFO.cli} (sdk ${BUILD_INFO.sdk}, blueprints ${BUILD_INFO.blueprints})`;
|
|
@@ -26837,18 +27128,6 @@ function parseTenantFlag(raw) {
|
|
|
26837
27128
|
}
|
|
26838
27129
|
return raw;
|
|
26839
27130
|
}
|
|
26840
|
-
var NUMBER_FLAG_INVALID = /* @__PURE__ */ Symbol("parseOptionalNumberFlag:invalid");
|
|
26841
|
-
function parseOptionalNumberFlag(flag, raw) {
|
|
26842
|
-
if (raw === void 0) return void 0;
|
|
26843
|
-
const n = Number(raw);
|
|
26844
|
-
if (!Number.isFinite(n)) {
|
|
26845
|
-
process.stderr.write(` \u2716 ${flag} must be a number (got '${raw}')
|
|
26846
|
-
`);
|
|
26847
|
-
process.exitCode = 2;
|
|
26848
|
-
return NUMBER_FLAG_INVALID;
|
|
26849
|
-
}
|
|
26850
|
-
return n;
|
|
26851
|
-
}
|
|
26852
27131
|
function toCommonOptions(flags) {
|
|
26853
27132
|
return {
|
|
26854
27133
|
env: flags.env,
|
|
@@ -26871,9 +27150,11 @@ function buildProgram(deps = {}) {
|
|
|
26871
27150
|
program.command("logout").description("Clear the stored login session.").action(async () => {
|
|
26872
27151
|
process.exitCode = await runLogout({ env: "production", json: false });
|
|
26873
27152
|
});
|
|
26874
|
-
const whoami = program.command("whoami").description("Show the authenticated session (tenant, env, principal).");
|
|
27153
|
+
const whoami = program.command("whoami").description("Show the authenticated session (tenant, env, principal).").option("--tenant <tenant>", "tenant to show: 'test' or 'live' (default live)");
|
|
26875
27154
|
addCommonAuthOptions(whoami).action(async (flags) => {
|
|
26876
|
-
|
|
27155
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27156
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27157
|
+
process.exitCode = await runWhoami({ ...toCommonOptions(flags), tenant }, deps);
|
|
26877
27158
|
});
|
|
26878
27159
|
const blueprintTest = program.command("blueprint-test").description("Apply a blueprint to a live env, assert it provisioned a working credential, then tear it down.").argument("<file>", "blueprint file path (./my.yaml) or a bundled blueprint name").option("--keep", "leave the provisioned graph in place (skip teardown) for inspection").option("--tenant <tenant>", "target tenant for the run: 'test' or 'live' (default live)");
|
|
26879
27160
|
addCommonAuthOptions(blueprintTest).action(
|
|
@@ -27020,7 +27301,7 @@ function registerContextCommands(program, deps) {
|
|
|
27020
27301
|
);
|
|
27021
27302
|
});
|
|
27022
27303
|
const destroy = context.command("destroy").description(
|
|
27023
|
-
"Permanently destroy an app context and ALL of its data (irreversible; asks for confirmation)."
|
|
27304
|
+
"Permanently destroy an app context and ALL of its data (irreversible; asks for confirmation). A blueprint's service principal belongs to the whole tenant and is NOT removed: delete it with 'identity delete'."
|
|
27024
27305
|
).argument("<contextId>", "the app context to destroy").option("--force", "skip the interactive confirmation (required when stdin is not a TTY)").option("--tenant <tenant>", "tenant the context lives in: 'test' or 'live' (default live)");
|
|
27025
27306
|
addCommonAuthOptions(destroy).action(
|
|
27026
27307
|
async (contextId, flags) => {
|
|
@@ -27175,15 +27456,11 @@ function registerIssuersCommands(program, deps) {
|
|
|
27175
27456
|
}
|
|
27176
27457
|
function registerProviderConfigCommands(program, deps) {
|
|
27177
27458
|
const providerConfig = program.command("provider-config").description("Manage BYOM (bring-your-own-model) tenant-level provider configs.");
|
|
27178
|
-
const create = providerConfig.command("create").description("Create a new BYOM provider config (not idempotent \u2014 a repeat --alias 400s).").requiredOption("--alias <alias>", "operator-facing name used to select this config; unique per tenant").requiredOption("--provider-type <type>", "'ANTHROPIC_NATIVE' or 'OPENAI_COMPATIBLE'").option("--endpoint <url>", "required for OPENAI_COMPATIBLE (no default); an optional override for ANTHROPIC_NATIVE").option("--declared-processing-region <region>", "customer-asserted, free-text, Vectros-unverified transparency field").option("--default-provider-model-id <id>", "convenience default only \u2014 a per-request model id, when the caller supplies one, always takes precedence").requiredOption("--api-key-secret-file <path>", "path to a file holding the provider credential ('-' for stdin) \u2014 never pass the secret as a bare flag value").requiredOption("--waiver-type <type>", "the waiver code this config is entitled under
|
|
27459
|
+
const create = providerConfig.command("create").description("Create a new BYOM provider config (not idempotent \u2014 a repeat --alias 400s).").requiredOption("--alias <alias>", "operator-facing name used to select this config; unique per tenant; 1-64 lowercase letters, digits, - or _").requiredOption("--provider-type <type>", "'ANTHROPIC_NATIVE' or 'OPENAI_COMPATIBLE'").option("--endpoint <url>", "required for OPENAI_COMPATIBLE (no default); an optional override for ANTHROPIC_NATIVE; https on port 443").option("--declared-processing-region <region>", "customer-asserted, free-text, Vectros-unverified transparency field").option("--default-provider-model-id <id>", "convenience default only \u2014 a per-request model id, when the caller supplies one, always takes precedence").requiredOption("--api-key-secret-file <path>", "path to a file holding the provider credential ('-' for stdin) \u2014 never pass the secret as a bare flag value").requiredOption("--waiver-type <type>", "the waiver code this config is entitled under; the platform accepts byo-model-inference:customer-key for a config you set up yourself").option("--billing-mode <mode>", "only 'CUSTOMER_DIRECT' (your provider bills you), which is also the default; 'PLATFORM_BILLED' is retired").option("--status <status>", "'ACTIVE' or 'DISABLED' (default ACTIVE) \u2014 ACTIVE requires the tenant already hold a granted waiver for --waiver-type").option("--tenant <tenant>", "tenant to create the config in: 'test' or 'live' (default live)");
|
|
27179
27460
|
addCommonAuthOptions(create).action(
|
|
27180
27461
|
async (flags) => {
|
|
27181
27462
|
const tenant = parseTenantFlag(flags.tenant);
|
|
27182
27463
|
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27183
|
-
const platformTokenFactor = parseOptionalNumberFlag("--platform-token-factor", flags.platformTokenFactor);
|
|
27184
|
-
if (platformTokenFactor === NUMBER_FLAG_INVALID) return;
|
|
27185
|
-
const platformOutputWeight = parseOptionalNumberFlag("--platform-output-weight", flags.platformOutputWeight);
|
|
27186
|
-
if (platformOutputWeight === NUMBER_FLAG_INVALID) return;
|
|
27187
27464
|
process.exitCode = await runProviderConfigCreate(
|
|
27188
27465
|
{
|
|
27189
27466
|
...toCommonOptions(flags),
|
|
@@ -27195,8 +27472,6 @@ function registerProviderConfigCommands(program, deps) {
|
|
|
27195
27472
|
apiKeySecretFile: flags.apiKeySecretFile,
|
|
27196
27473
|
waiverType: flags.waiverType,
|
|
27197
27474
|
billingMode: flags.billingMode,
|
|
27198
|
-
platformTokenFactor,
|
|
27199
|
-
platformOutputWeight,
|
|
27200
27475
|
status: flags.status,
|
|
27201
27476
|
tenant
|
|
27202
27477
|
},
|
|
@@ -27214,13 +27489,9 @@ function registerProviderConfigCommands(program, deps) {
|
|
|
27214
27489
|
addCommonAuthOptions(get).action(async (id, flags) => {
|
|
27215
27490
|
process.exitCode = await runProviderConfigGet({ ...toCommonOptions(flags), id }, deps);
|
|
27216
27491
|
});
|
|
27217
|
-
const update = providerConfig.command("update").description("Patch a provider config (partial update \u2014 an omitted field is left unchanged).").argument("<id>").option("--alias <alias>", "rename this config (
|
|
27492
|
+
const update = providerConfig.command("update").description("Patch a provider config (partial update \u2014 an omitted field is left unchanged).").argument("<id>").option("--alias <alias>", "rename this config (unique per tenant); the platform refuses a new alias that is not 1-64 lowercase letters, digits, - or _ (an alias the config already holds is accepted)").option("--provider-type <type>", "'ANTHROPIC_NATIVE' or 'OPENAI_COMPATIBLE'; changing the type of an existing config needs --endpoint stated in the same command (--endpoint '' means the provider's default), or the platform answers 400").option("--endpoint <url>", "update the provider endpoint; https on port 443; changing the host needs --api-key-secret-file again; '' clears it (the provider's default), which also changes the host").option("--declared-processing-region <region>", "update the transparency field").option("--default-provider-model-id <id>", "update the convenience default model id").option("--api-key-secret-file <path>", "rotate the provider credential from a file ('-' for stdin); omit to leave the stored secret unchanged (required when --endpoint changes the host)").option("--waiver-type <type>", "update the waiver code this config is entitled under; the platform refuses a change to anything but byo-model-inference:customer-key (a value the config already holds is accepted)").option("--billing-mode <mode>", "only 'CUSTOMER_DIRECT' (your provider bills you); 'PLATFORM_BILLED' is retired. Omit it to leave the stored mode unchanged").option("--status <status>", "'ACTIVE' or 'DISABLED' \u2014 activating requires a granted waiver for this config's waiverType");
|
|
27218
27493
|
addCommonAuthOptions(update).action(
|
|
27219
27494
|
async (id, flags) => {
|
|
27220
|
-
const platformTokenFactor = parseOptionalNumberFlag("--platform-token-factor", flags.platformTokenFactor);
|
|
27221
|
-
if (platformTokenFactor === NUMBER_FLAG_INVALID) return;
|
|
27222
|
-
const platformOutputWeight = parseOptionalNumberFlag("--platform-output-weight", flags.platformOutputWeight);
|
|
27223
|
-
if (platformOutputWeight === NUMBER_FLAG_INVALID) return;
|
|
27224
27495
|
process.exitCode = await runProviderConfigUpdate(
|
|
27225
27496
|
{
|
|
27226
27497
|
...toCommonOptions(flags),
|
|
@@ -27233,8 +27504,6 @@ function registerProviderConfigCommands(program, deps) {
|
|
|
27233
27504
|
apiKeySecretFile: flags.apiKeySecretFile,
|
|
27234
27505
|
waiverType: flags.waiverType,
|
|
27235
27506
|
billingMode: flags.billingMode,
|
|
27236
|
-
platformTokenFactor,
|
|
27237
|
-
platformOutputWeight,
|
|
27238
27507
|
status: flags.status
|
|
27239
27508
|
},
|
|
27240
27509
|
deps
|
|
@@ -27502,12 +27771,15 @@ function dimensionOption() {
|
|
|
27502
27771
|
).makeOptionMandatory();
|
|
27503
27772
|
}
|
|
27504
27773
|
function registerIdentityCommands(program, deps) {
|
|
27505
|
-
const identity = program.command("identity").description("Manage tenant-wide
|
|
27506
|
-
const create = identity.command("create").description("Create (idempotent by externalId) a principal.").addOption(dimensionOption()).requiredOption("--external-id <id>", "your stable id for the principal (idempotency key)").option("--name <name>", "display name (entity namespaces only, not --type user)").option("--email <email>", "email (user only)").option("--service", "user only: mint a SERVICE principal (default HUMAN)").option("--scope <ns:value>", "entity only: a parent ownership edge (repeatable, \u22642, e.g. org:<id>)", collect, []).option("--metadata <json>", "JSON object of metadata");
|
|
27774
|
+
const identity = program.command("identity").description("Manage principals: users (tenant-wide) and namespaced entities (an entity lives with its namespace) \u2014 reference, never embed.");
|
|
27775
|
+
const create = identity.command("create").description("Create (idempotent by externalId) a principal.").addOption(dimensionOption()).requiredOption("--external-id <id>", "your stable id for the principal (idempotency key)").option("--name <name>", "display name (entity namespaces only, not --type user)").option("--email <email>", "email (user only)").option("--service", "user only: mint a SERVICE principal (default HUMAN)").option("--scope <ns:value>", "entity only: a parent ownership edge (repeatable, \u22642, e.g. org:<id>)", collect, []).option("--metadata <json>", "JSON object of metadata").option("--tenant <tenant>", "tenant to target: 'test' or 'live' (default live)");
|
|
27507
27776
|
addCommonAuthOptions(create).action(
|
|
27508
27777
|
async (flags) => {
|
|
27778
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27779
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27509
27780
|
process.exitCode = await runIdentityCreate({
|
|
27510
27781
|
...toCommonOptions(flags),
|
|
27782
|
+
tenant,
|
|
27511
27783
|
type: flags.type,
|
|
27512
27784
|
externalId: flags.externalId,
|
|
27513
27785
|
name: flags.name,
|
|
@@ -27518,11 +27790,14 @@ function registerIdentityCommands(program, deps) {
|
|
|
27518
27790
|
}, deps);
|
|
27519
27791
|
}
|
|
27520
27792
|
);
|
|
27521
|
-
const list = identity.command("list").description("List principals of a dimension (optionally filtered).").addOption(dimensionOption()).option("--external-id <id>", "filter to a stable id").option("--limit <n>", "page size (1\u2013100)", (v) => Number.parseInt(v, 10));
|
|
27793
|
+
const list = identity.command("list").description("List principals of a dimension (optionally filtered).").addOption(dimensionOption()).option("--external-id <id>", "filter to a stable id").option("--limit <n>", "page size (1\u2013100)", (v) => Number.parseInt(v, 10)).option("--tenant <tenant>", "tenant to target: 'test' or 'live' (default live)");
|
|
27522
27794
|
addCommonAuthOptions(list).action(
|
|
27523
27795
|
async (flags) => {
|
|
27796
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27797
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27524
27798
|
process.exitCode = await runIdentityList({
|
|
27525
27799
|
...toCommonOptions(flags),
|
|
27800
|
+
tenant,
|
|
27526
27801
|
type: flags.type,
|
|
27527
27802
|
externalId: flags.externalId,
|
|
27528
27803
|
limit: flags.limit
|
|
@@ -27542,20 +27817,30 @@ function registerIdentityCommands(program, deps) {
|
|
|
27542
27817
|
);
|
|
27543
27818
|
}
|
|
27544
27819
|
);
|
|
27545
|
-
const get = identity.command("get").description("Show one principal by its Vectros id.").addOption(dimensionOption()).requiredOption("--id <vectrosId>", "the Vectros-assigned id (from create/list)");
|
|
27820
|
+
const get = identity.command("get").description("Show one principal by its Vectros id.").addOption(dimensionOption()).requiredOption("--id <vectrosId>", "the Vectros-assigned id (from create/list)").option("--tenant <tenant>", "tenant to target: 'test' or 'live' (default live)");
|
|
27546
27821
|
addCommonAuthOptions(get).action(async (flags) => {
|
|
27547
|
-
|
|
27822
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27823
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27824
|
+
process.exitCode = await runIdentityGet({ ...toCommonOptions(flags), tenant, type: flags.type, id: flags.id }, deps);
|
|
27548
27825
|
});
|
|
27549
|
-
const del = identity.command("delete").description("Delete a principal by its Vectros id.").addOption(dimensionOption()).requiredOption("--id <vectrosId>", "the Vectros-assigned id");
|
|
27826
|
+
const del = identity.command("delete").description("Delete a principal by its Vectros id.").addOption(dimensionOption()).requiredOption("--id <vectrosId>", "the Vectros-assigned id").option("--tenant <tenant>", "tenant to target: 'test' or 'live' (default live)");
|
|
27550
27827
|
addAuthOptions(del).action(async (flags) => {
|
|
27551
|
-
|
|
27828
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27829
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27830
|
+
process.exitCode = await runIdentityDelete(
|
|
27831
|
+
{ ...toCommonOptions(flags), tenant, type: flags.type, id: flags.id },
|
|
27832
|
+
deps
|
|
27833
|
+
);
|
|
27552
27834
|
});
|
|
27553
|
-
const invite = identity.command("invite").description("Onboard a person into a context by email (POST /v1/users/invite) \u2014 no root key required.").requiredOption("--context <contextId>", "the app context the invitee will operate in").requiredOption("--role <roleId>", "an existing Role in that context; the invitee inherits its scopes").requiredOption("--email <email>", "the invitee's email address").option("--first-name <name>", "personalizes the invitation email greeting").option("--accept-url <url>", "your accept-invite landing page (required unless --no-send-email)").option("--no-send-email", "skip platform delivery; the response carries the raw inviteToken/acceptLink instead");
|
|
27835
|
+
const invite = identity.command("invite").description("Onboard a person into a context by email (POST /v1/users/invite) \u2014 no root key required.").requiredOption("--context <contextId>", "the app context the invitee will operate in").requiredOption("--role <roleId>", "an existing Role in that context; the invitee inherits its scopes").requiredOption("--email <email>", "the invitee's email address").option("--first-name <name>", "personalizes the invitation email greeting").option("--accept-url <url>", "your accept-invite landing page (required unless --no-send-email)").option("--no-send-email", "skip platform delivery; the response carries the raw inviteToken/acceptLink instead").option("--tenant <tenant>", "tenant to target: 'test' or 'live' (default live)");
|
|
27554
27836
|
addCommonAuthOptions(invite).action(
|
|
27555
27837
|
async (flags) => {
|
|
27838
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27839
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27556
27840
|
process.exitCode = await runIdentityInvite(
|
|
27557
27841
|
{
|
|
27558
27842
|
...toCommonOptions(flags),
|
|
27843
|
+
tenant,
|
|
27559
27844
|
context: flags.context,
|
|
27560
27845
|
role: flags.role,
|
|
27561
27846
|
email: flags.email,
|