@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.mjs
CHANGED
|
@@ -1619,7 +1619,7 @@ var require_proper_lockfile = __commonJS({
|
|
|
1619
1619
|
// src/cli-bootstrap.ts
|
|
1620
1620
|
import * as readline2 from "readline/promises";
|
|
1621
1621
|
|
|
1622
|
-
// node_modules/@vectros-ai/sdk/dist/index.mjs
|
|
1622
|
+
// ../../node_modules/@vectros-ai/sdk/dist/index.mjs
|
|
1623
1623
|
var __defProp2 = Object.defineProperty;
|
|
1624
1624
|
var __export = (target, all) => {
|
|
1625
1625
|
for (var name in all)
|
|
@@ -1634,9 +1634,11 @@ __export(api_exports, {
|
|
|
1634
1634
|
BatchWriteRequest: () => BatchWriteRequest,
|
|
1635
1635
|
BatchWriteResult: () => BatchWriteResult,
|
|
1636
1636
|
ChatMessage: () => ChatMessage,
|
|
1637
|
+
ChatRequest: () => ChatRequest,
|
|
1637
1638
|
ConflictError: () => ConflictError,
|
|
1638
1639
|
ContentDeltaEvent: () => ContentDeltaEvent,
|
|
1639
1640
|
ContentTooLargeError: () => ContentTooLargeError,
|
|
1641
|
+
DocumentAskRequest: () => DocumentAskRequest,
|
|
1640
1642
|
DocumentContextEvent: () => DocumentContextEvent,
|
|
1641
1643
|
DocumentLookupRequest: () => DocumentLookupRequest,
|
|
1642
1644
|
DocumentPatchRequest: () => DocumentPatchRequest,
|
|
@@ -1667,8 +1669,10 @@ __export(api_exports, {
|
|
|
1667
1669
|
NotImplementedError: () => NotImplementedError,
|
|
1668
1670
|
PaymentRequiredError: () => PaymentRequiredError,
|
|
1669
1671
|
PingResponse: () => PingResponse,
|
|
1672
|
+
RagRequest: () => RagRequest,
|
|
1670
1673
|
RagSearch: () => RagSearch,
|
|
1671
1674
|
ReadAccessLogResponse: () => ReadAccessLogResponse,
|
|
1675
|
+
ReasoningDeltaEvent: () => ReasoningDeltaEvent,
|
|
1672
1676
|
RecordLookupRequest: () => RecordLookupRequest,
|
|
1673
1677
|
RecordRequest: () => RecordRequest,
|
|
1674
1678
|
RecordResponse: () => RecordResponse,
|
|
@@ -2024,7 +2028,33 @@ var ListUsersRequestOrder = {
|
|
|
2024
2028
|
Asc: "asc",
|
|
2025
2029
|
Desc: "desc"
|
|
2026
2030
|
};
|
|
2031
|
+
var ChatRequest;
|
|
2032
|
+
((ChatRequest2) => {
|
|
2033
|
+
ChatRequest2.Reasoning = {
|
|
2034
|
+
Omit: "omit",
|
|
2035
|
+
Stream: "stream"
|
|
2036
|
+
};
|
|
2037
|
+
})(ChatRequest || (ChatRequest = {}));
|
|
2038
|
+
var DocumentAskRequest;
|
|
2039
|
+
((DocumentAskRequest2) => {
|
|
2040
|
+
DocumentAskRequest2.Reasoning = {
|
|
2041
|
+
Omit: "omit",
|
|
2042
|
+
Stream: "stream"
|
|
2043
|
+
};
|
|
2044
|
+
})(DocumentAskRequest || (DocumentAskRequest = {}));
|
|
2045
|
+
var RagRequest;
|
|
2046
|
+
((RagRequest2) => {
|
|
2047
|
+
RagRequest2.Reasoning = {
|
|
2048
|
+
Omit: "omit",
|
|
2049
|
+
Stream: "stream"
|
|
2050
|
+
};
|
|
2051
|
+
})(RagRequest || (RagRequest = {}));
|
|
2027
2052
|
var inference_exports = {};
|
|
2053
|
+
__export(inference_exports, {
|
|
2054
|
+
ChatRequest: () => ChatRequest,
|
|
2055
|
+
DocumentAskRequest: () => DocumentAskRequest,
|
|
2056
|
+
RagRequest: () => RagRequest
|
|
2057
|
+
});
|
|
2028
2058
|
var BatchWriteRequest;
|
|
2029
2059
|
((BatchWriteRequest2) => {
|
|
2030
2060
|
BatchWriteRequest2.Atomicity = {
|
|
@@ -2297,6 +2327,12 @@ var ReadAccessLogResponse;
|
|
|
2297
2327
|
Rag: "rag"
|
|
2298
2328
|
};
|
|
2299
2329
|
})(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
|
|
2330
|
+
var ReasoningDeltaEvent;
|
|
2331
|
+
((ReasoningDeltaEvent2) => {
|
|
2332
|
+
ReasoningDeltaEvent2.Event = {
|
|
2333
|
+
ReasoningDelta: "reasoning_delta"
|
|
2334
|
+
};
|
|
2335
|
+
})(ReasoningDeltaEvent || (ReasoningDeltaEvent = {}));
|
|
2300
2336
|
var RecordRequest;
|
|
2301
2337
|
((RecordRequest2) => {
|
|
2302
2338
|
RecordRequest2.Status = {
|
|
@@ -4049,7 +4085,7 @@ var AuthClient = class {
|
|
|
4049
4085
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/jwks");
|
|
4050
4086
|
}
|
|
4051
4087
|
/**
|
|
4052
|
-
* 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.
|
|
4088
|
+
* 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.
|
|
4053
4089
|
*
|
|
4054
4090
|
* @param {Vectros.GetAccessLogRequest} request
|
|
4055
4091
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4191,7 +4227,7 @@ var AuthClient = class {
|
|
|
4191
4227
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/admin/keys/scoped");
|
|
4192
4228
|
}
|
|
4193
4229
|
/**
|
|
4194
|
-
* 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
|
|
4230
|
+
* 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.
|
|
4195
4231
|
*
|
|
4196
4232
|
* @param {Vectros.CreateScopedKeyRequest} request
|
|
4197
4233
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4534,6 +4570,7 @@ var AuthClient = class {
|
|
|
4534
4570
|
* @throws {@link Vectros.BadRequestError}
|
|
4535
4571
|
* @throws {@link Vectros.ForbiddenError}
|
|
4536
4572
|
* @throws {@link Vectros.NotFoundError}
|
|
4573
|
+
* @throws {@link Vectros.ConflictError}
|
|
4537
4574
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4538
4575
|
*
|
|
4539
4576
|
* @example
|
|
@@ -4586,6 +4623,8 @@ var AuthClient = class {
|
|
|
4586
4623
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4587
4624
|
case 404:
|
|
4588
4625
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4626
|
+
case 409:
|
|
4627
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4589
4628
|
case 429:
|
|
4590
4629
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4591
4630
|
default:
|
|
@@ -4669,6 +4708,8 @@ var AuthClient = class {
|
|
|
4669
4708
|
* @throws {@link Vectros.BadRequestError}
|
|
4670
4709
|
* @throws {@link Vectros.PaymentRequiredError}
|
|
4671
4710
|
* @throws {@link Vectros.ForbiddenError}
|
|
4711
|
+
* @throws {@link Vectros.NotFoundError}
|
|
4712
|
+
* @throws {@link Vectros.ConflictError}
|
|
4672
4713
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4673
4714
|
*
|
|
4674
4715
|
* @example
|
|
@@ -4721,6 +4762,10 @@ var AuthClient = class {
|
|
|
4721
4762
|
throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
|
|
4722
4763
|
case 403:
|
|
4723
4764
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4765
|
+
case 404:
|
|
4766
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4767
|
+
case 409:
|
|
4768
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4724
4769
|
case 429:
|
|
4725
4770
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4726
4771
|
default:
|
|
@@ -4809,6 +4854,7 @@ var AuthClient = class {
|
|
|
4809
4854
|
* @throws {@link Vectros.BadRequestError}
|
|
4810
4855
|
* @throws {@link Vectros.ForbiddenError}
|
|
4811
4856
|
* @throws {@link Vectros.NotFoundError}
|
|
4857
|
+
* @throws {@link Vectros.ConflictError}
|
|
4812
4858
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4813
4859
|
*
|
|
4814
4860
|
* @example
|
|
@@ -4865,6 +4911,8 @@ var AuthClient = class {
|
|
|
4865
4911
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4866
4912
|
case 404:
|
|
4867
4913
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
4914
|
+
case 409:
|
|
4915
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
4868
4916
|
case 429:
|
|
4869
4917
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
4870
4918
|
default:
|
|
@@ -4955,6 +5003,7 @@ var AuthClient = class {
|
|
|
4955
5003
|
* @throws {@link Vectros.BadRequestError}
|
|
4956
5004
|
* @throws {@link Vectros.ForbiddenError}
|
|
4957
5005
|
* @throws {@link Vectros.NotFoundError}
|
|
5006
|
+
* @throws {@link Vectros.ConflictError}
|
|
4958
5007
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4959
5008
|
*
|
|
4960
5009
|
* @example
|
|
@@ -5005,6 +5054,8 @@ var AuthClient = class {
|
|
|
5005
5054
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5006
5055
|
case 404:
|
|
5007
5056
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5057
|
+
case 409:
|
|
5058
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5008
5059
|
case 429:
|
|
5009
5060
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5010
5061
|
default:
|
|
@@ -5151,13 +5202,14 @@ var AuthClient = class {
|
|
|
5151
5202
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/app-contexts/{contextId}");
|
|
5152
5203
|
}
|
|
5153
5204
|
/**
|
|
5154
|
-
* 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
|
|
5205
|
+
* 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.
|
|
5155
5206
|
*
|
|
5156
5207
|
* @param {Vectros.UpdateAppContextRequest} request
|
|
5157
5208
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
5158
5209
|
*
|
|
5159
5210
|
* @throws {@link Vectros.ForbiddenError}
|
|
5160
5211
|
* @throws {@link Vectros.NotFoundError}
|
|
5212
|
+
* @throws {@link Vectros.ConflictError}
|
|
5161
5213
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5162
5214
|
*
|
|
5163
5215
|
* @example
|
|
@@ -5206,6 +5258,8 @@ var AuthClient = class {
|
|
|
5206
5258
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5207
5259
|
case 404:
|
|
5208
5260
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5261
|
+
case 409:
|
|
5262
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5209
5263
|
case 429:
|
|
5210
5264
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5211
5265
|
default:
|
|
@@ -5362,6 +5416,7 @@ var AuthClient = class {
|
|
|
5362
5416
|
*
|
|
5363
5417
|
* @throws {@link Vectros.ForbiddenError}
|
|
5364
5418
|
* @throws {@link Vectros.NotFoundError}
|
|
5419
|
+
* @throws {@link Vectros.ConflictError}
|
|
5365
5420
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5366
5421
|
*
|
|
5367
5422
|
* @example
|
|
@@ -5414,6 +5469,8 @@ var AuthClient = class {
|
|
|
5414
5469
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5415
5470
|
case 404:
|
|
5416
5471
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5472
|
+
case 409:
|
|
5473
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5417
5474
|
case 429:
|
|
5418
5475
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5419
5476
|
default:
|
|
@@ -5439,6 +5496,7 @@ var AuthClient = class {
|
|
|
5439
5496
|
*
|
|
5440
5497
|
* @throws {@link Vectros.ForbiddenError}
|
|
5441
5498
|
* @throws {@link Vectros.NotFoundError}
|
|
5499
|
+
* @throws {@link Vectros.ConflictError}
|
|
5442
5500
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5443
5501
|
*
|
|
5444
5502
|
* @example
|
|
@@ -5481,6 +5539,8 @@ var AuthClient = class {
|
|
|
5481
5539
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5482
5540
|
case 404:
|
|
5483
5541
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5542
|
+
case 409:
|
|
5543
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5484
5544
|
case 429:
|
|
5485
5545
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5486
5546
|
default:
|
|
@@ -5754,7 +5814,7 @@ var AuthClient = class {
|
|
|
5754
5814
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/issuers/{issuerId}");
|
|
5755
5815
|
}
|
|
5756
5816
|
/**
|
|
5757
|
-
* 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
|
|
5817
|
+
* 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.
|
|
5758
5818
|
*
|
|
5759
5819
|
* @param {Vectros.IssuerUpdateRequest} request
|
|
5760
5820
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -5762,6 +5822,7 @@ var AuthClient = class {
|
|
|
5762
5822
|
* @throws {@link Vectros.BadRequestError}
|
|
5763
5823
|
* @throws {@link Vectros.ForbiddenError}
|
|
5764
5824
|
* @throws {@link Vectros.NotFoundError}
|
|
5825
|
+
* @throws {@link Vectros.ConflictError}
|
|
5765
5826
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
5766
5827
|
*
|
|
5767
5828
|
* @example
|
|
@@ -5808,6 +5869,8 @@ var AuthClient = class {
|
|
|
5808
5869
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
5809
5870
|
case 404:
|
|
5810
5871
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
5872
|
+
case 409:
|
|
5873
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
5811
5874
|
case 429:
|
|
5812
5875
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
5813
5876
|
default:
|
|
@@ -5821,7 +5884,7 @@ var AuthClient = class {
|
|
|
5821
5884
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/v1/auth/issuers/{issuerId}");
|
|
5822
5885
|
}
|
|
5823
5886
|
/**
|
|
5824
|
-
* 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
|
|
5887
|
+
* 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.
|
|
5825
5888
|
*
|
|
5826
5889
|
* @param {Vectros.DeleteIssuerRequest} request
|
|
5827
5890
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -5947,7 +6010,7 @@ var AuthClient = class {
|
|
|
5947
6010
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/issuers");
|
|
5948
6011
|
}
|
|
5949
6012
|
/**
|
|
5950
|
-
* 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
|
|
6013
|
+
* 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.
|
|
5951
6014
|
*
|
|
5952
6015
|
* @param {Vectros.IssuerRequest} request
|
|
5953
6016
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6356,6 +6419,7 @@ var AuthClient = class {
|
|
|
6356
6419
|
* @throws {@link Vectros.BadRequestError}
|
|
6357
6420
|
* @throws {@link Vectros.ForbiddenError}
|
|
6358
6421
|
* @throws {@link Vectros.NotFoundError}
|
|
6422
|
+
* @throws {@link Vectros.ConflictError}
|
|
6359
6423
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
6360
6424
|
*
|
|
6361
6425
|
* @example
|
|
@@ -6403,6 +6467,8 @@ var AuthClient = class {
|
|
|
6403
6467
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
6404
6468
|
case 404:
|
|
6405
6469
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
6470
|
+
case 409:
|
|
6471
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
6406
6472
|
case 429:
|
|
6407
6473
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
6408
6474
|
default:
|
|
@@ -6502,6 +6568,7 @@ var AuthClient = class {
|
|
|
6502
6568
|
* @throws {@link Vectros.UnauthorizedError}
|
|
6503
6569
|
* @throws {@link Vectros.ForbiddenError}
|
|
6504
6570
|
* @throws {@link Vectros.NotFoundError}
|
|
6571
|
+
* @throws {@link Vectros.ContentTooLargeError}
|
|
6505
6572
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
6506
6573
|
*
|
|
6507
6574
|
* @example
|
|
@@ -6546,6 +6613,8 @@ var AuthClient = class {
|
|
|
6546
6613
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
6547
6614
|
case 404:
|
|
6548
6615
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
6616
|
+
case 413:
|
|
6617
|
+
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
6549
6618
|
case 429:
|
|
6550
6619
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
6551
6620
|
default:
|
|
@@ -6564,7 +6633,7 @@ var ComplianceClient = class {
|
|
|
6564
6633
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
6565
6634
|
}
|
|
6566
6635
|
/**
|
|
6567
|
-
* Submits
|
|
6636
|
+
* 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.
|
|
6568
6637
|
*
|
|
6569
6638
|
* @param {Vectros.ErasureRequest} request
|
|
6570
6639
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6627,7 +6696,7 @@ var ComplianceClient = class {
|
|
|
6627
6696
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/erasure-requests");
|
|
6628
6697
|
}
|
|
6629
6698
|
/**
|
|
6630
|
-
* 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
|
|
6699
|
+
* 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.
|
|
6631
6700
|
*
|
|
6632
6701
|
* @param {Vectros.GetErasureRequestRequest} request
|
|
6633
6702
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6685,7 +6754,7 @@ var ComplianceClient = class {
|
|
|
6685
6754
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/erasure-requests/{id}");
|
|
6686
6755
|
}
|
|
6687
6756
|
/**
|
|
6688
|
-
*
|
|
6757
|
+
* 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.
|
|
6689
6758
|
*
|
|
6690
6759
|
* @param {Vectros.ExportRequest} request
|
|
6691
6760
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6746,7 +6815,7 @@ var ComplianceClient = class {
|
|
|
6746
6815
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/admin/export");
|
|
6747
6816
|
}
|
|
6748
6817
|
/**
|
|
6749
|
-
*
|
|
6818
|
+
* 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.
|
|
6750
6819
|
*
|
|
6751
6820
|
* @param {Vectros.GetExportRequest} request
|
|
6752
6821
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6877,6 +6946,8 @@ var DocumentsClient = class {
|
|
|
6877
6946
|
*
|
|
6878
6947
|
* @throws {@link Vectros.BadRequestError}
|
|
6879
6948
|
* @throws {@link Vectros.ForbiddenError}
|
|
6949
|
+
* @throws {@link Vectros.NotFoundError}
|
|
6950
|
+
* @throws {@link Vectros.ConflictError}
|
|
6880
6951
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
6881
6952
|
*
|
|
6882
6953
|
* @example
|
|
@@ -6928,6 +6999,10 @@ var DocumentsClient = class {
|
|
|
6928
6999
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
6929
7000
|
case 403:
|
|
6930
7001
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7002
|
+
case 404:
|
|
7003
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7004
|
+
case 409:
|
|
7005
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
6931
7006
|
case 429:
|
|
6932
7007
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
6933
7008
|
default:
|
|
@@ -7075,6 +7150,7 @@ var DocumentsClient = class {
|
|
|
7075
7150
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
7076
7151
|
*
|
|
7077
7152
|
* @throws {@link Vectros.NotFoundError}
|
|
7153
|
+
* @throws {@link Vectros.ConflictError}
|
|
7078
7154
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7079
7155
|
*
|
|
7080
7156
|
* @example
|
|
@@ -7114,6 +7190,8 @@ var DocumentsClient = class {
|
|
|
7114
7190
|
switch (_response.error.statusCode) {
|
|
7115
7191
|
case 404:
|
|
7116
7192
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7193
|
+
case 409:
|
|
7194
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7117
7195
|
case 429:
|
|
7118
7196
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7119
7197
|
default:
|
|
@@ -7406,7 +7484,7 @@ var DocumentsClient = class {
|
|
|
7406
7484
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/download");
|
|
7407
7485
|
}
|
|
7408
7486
|
/**
|
|
7409
|
-
* Returns the document's full text body when it is retained:
|
|
7487
|
+
* 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.
|
|
7410
7488
|
*
|
|
7411
7489
|
* @param {Vectros.GetDocumentTextRequest} request
|
|
7412
7490
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7526,6 +7604,7 @@ var DocumentsClient = class {
|
|
|
7526
7604
|
*
|
|
7527
7605
|
* @throws {@link Vectros.BadRequestError}
|
|
7528
7606
|
* @throws {@link Vectros.ForbiddenError}
|
|
7607
|
+
* @throws {@link Vectros.ConflictError}
|
|
7529
7608
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7530
7609
|
*
|
|
7531
7610
|
* @example
|
|
@@ -7575,6 +7654,8 @@ var DocumentsClient = class {
|
|
|
7575
7654
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7576
7655
|
case 403:
|
|
7577
7656
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7657
|
+
case 409:
|
|
7658
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7578
7659
|
case 429:
|
|
7579
7660
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7580
7661
|
default:
|
|
@@ -7658,6 +7739,8 @@ var FoldersClient = class {
|
|
|
7658
7739
|
*
|
|
7659
7740
|
* @throws {@link Vectros.BadRequestError}
|
|
7660
7741
|
* @throws {@link Vectros.ForbiddenError}
|
|
7742
|
+
* @throws {@link Vectros.NotFoundError}
|
|
7743
|
+
* @throws {@link Vectros.ConflictError}
|
|
7661
7744
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7662
7745
|
*
|
|
7663
7746
|
* @example
|
|
@@ -7707,6 +7790,10 @@ var FoldersClient = class {
|
|
|
7707
7790
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7708
7791
|
case 403:
|
|
7709
7792
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7793
|
+
case 404:
|
|
7794
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7795
|
+
case 409:
|
|
7796
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7710
7797
|
case 429:
|
|
7711
7798
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7712
7799
|
default:
|
|
@@ -7782,6 +7869,7 @@ var FoldersClient = class {
|
|
|
7782
7869
|
*
|
|
7783
7870
|
* @throws {@link Vectros.ForbiddenError}
|
|
7784
7871
|
* @throws {@link Vectros.NotFoundError}
|
|
7872
|
+
* @throws {@link Vectros.ConflictError}
|
|
7785
7873
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7786
7874
|
*
|
|
7787
7875
|
* @example
|
|
@@ -7829,6 +7917,8 @@ var FoldersClient = class {
|
|
|
7829
7917
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
7830
7918
|
case 404:
|
|
7831
7919
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7920
|
+
case 409:
|
|
7921
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7832
7922
|
case 429:
|
|
7833
7923
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7834
7924
|
default:
|
|
@@ -7849,6 +7939,7 @@ var FoldersClient = class {
|
|
|
7849
7939
|
*
|
|
7850
7940
|
* @throws {@link Vectros.BadRequestError}
|
|
7851
7941
|
* @throws {@link Vectros.NotFoundError}
|
|
7942
|
+
* @throws {@link Vectros.ConflictError}
|
|
7852
7943
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
7853
7944
|
*
|
|
7854
7945
|
* @example
|
|
@@ -7890,6 +7981,8 @@ var FoldersClient = class {
|
|
|
7890
7981
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
7891
7982
|
case 404:
|
|
7892
7983
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
7984
|
+
case 409:
|
|
7985
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
7893
7986
|
case 429:
|
|
7894
7987
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
7895
7988
|
default:
|
|
@@ -7903,7 +7996,7 @@ var FoldersClient = class {
|
|
|
7903
7996
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/folders/{id}");
|
|
7904
7997
|
}
|
|
7905
7998
|
/**
|
|
7906
|
-
* 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
|
|
7999
|
+
* 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.
|
|
7907
8000
|
*
|
|
7908
8001
|
* @param {Vectros.PatchFolderRequest} request
|
|
7909
8002
|
* @param {FoldersClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -8126,6 +8219,8 @@ var IdentityClient = class {
|
|
|
8126
8219
|
*
|
|
8127
8220
|
* @throws {@link Vectros.BadRequestError}
|
|
8128
8221
|
* @throws {@link Vectros.ForbiddenError}
|
|
8222
|
+
* @throws {@link Vectros.NotFoundError}
|
|
8223
|
+
* @throws {@link Vectros.ConflictError}
|
|
8129
8224
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8130
8225
|
*
|
|
8131
8226
|
* @example
|
|
@@ -8178,6 +8273,10 @@ var IdentityClient = class {
|
|
|
8178
8273
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
8179
8274
|
case 403:
|
|
8180
8275
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
8276
|
+
case 404:
|
|
8277
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8278
|
+
case 409:
|
|
8279
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8181
8280
|
case 429:
|
|
8182
8281
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8183
8282
|
default:
|
|
@@ -8258,6 +8357,7 @@ var IdentityClient = class {
|
|
|
8258
8357
|
*
|
|
8259
8358
|
* @throws {@link Vectros.BadRequestError}
|
|
8260
8359
|
* @throws {@link Vectros.NotFoundError}
|
|
8360
|
+
* @throws {@link Vectros.ConflictError}
|
|
8261
8361
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8262
8362
|
*
|
|
8263
8363
|
* @example
|
|
@@ -8310,6 +8410,8 @@ var IdentityClient = class {
|
|
|
8310
8410
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
8311
8411
|
case 404:
|
|
8312
8412
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8413
|
+
case 409:
|
|
8414
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8313
8415
|
case 429:
|
|
8314
8416
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8315
8417
|
default:
|
|
@@ -8329,6 +8431,7 @@ var IdentityClient = class {
|
|
|
8329
8431
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
8330
8432
|
*
|
|
8331
8433
|
* @throws {@link Vectros.NotFoundError}
|
|
8434
|
+
* @throws {@link Vectros.ConflictError}
|
|
8332
8435
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8333
8436
|
*
|
|
8334
8437
|
* @example
|
|
@@ -8373,6 +8476,8 @@ var IdentityClient = class {
|
|
|
8373
8476
|
switch (_response.error.statusCode) {
|
|
8374
8477
|
case 404:
|
|
8375
8478
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8479
|
+
case 409:
|
|
8480
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8376
8481
|
case 429:
|
|
8377
8482
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8378
8483
|
default:
|
|
@@ -8596,6 +8701,7 @@ var IdentityClient = class {
|
|
|
8596
8701
|
*
|
|
8597
8702
|
* @throws {@link Vectros.ForbiddenError}
|
|
8598
8703
|
* @throws {@link Vectros.NotFoundError}
|
|
8704
|
+
* @throws {@link Vectros.ConflictError}
|
|
8599
8705
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8600
8706
|
*
|
|
8601
8707
|
* @example
|
|
@@ -8647,6 +8753,8 @@ var IdentityClient = class {
|
|
|
8647
8753
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
8648
8754
|
case 404:
|
|
8649
8755
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8756
|
+
case 409:
|
|
8757
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8650
8758
|
case 429:
|
|
8651
8759
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8652
8760
|
default:
|
|
@@ -8667,6 +8775,7 @@ var IdentityClient = class {
|
|
|
8667
8775
|
*
|
|
8668
8776
|
* @throws {@link Vectros.ForbiddenError}
|
|
8669
8777
|
* @throws {@link Vectros.NotFoundError}
|
|
8778
|
+
* @throws {@link Vectros.ConflictError}
|
|
8670
8779
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8671
8780
|
*
|
|
8672
8781
|
* @example
|
|
@@ -8711,6 +8820,8 @@ var IdentityClient = class {
|
|
|
8711
8820
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
8712
8821
|
case 404:
|
|
8713
8822
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
8823
|
+
case 409:
|
|
8824
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8714
8825
|
case 429:
|
|
8715
8826
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8716
8827
|
default:
|
|
@@ -8775,7 +8886,7 @@ var IdentityClient = class {
|
|
|
8775
8886
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/namespaces");
|
|
8776
8887
|
}
|
|
8777
8888
|
/**
|
|
8778
|
-
* 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
|
|
8889
|
+
* 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.
|
|
8779
8890
|
*
|
|
8780
8891
|
* @param {Vectros.RegisterNamespaceRequest} request
|
|
8781
8892
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -8917,6 +9028,7 @@ var IdentityClient = class {
|
|
|
8917
9028
|
*
|
|
8918
9029
|
* @throws {@link Vectros.BadRequestError}
|
|
8919
9030
|
* @throws {@link Vectros.ForbiddenError}
|
|
9031
|
+
* @throws {@link Vectros.ConflictError}
|
|
8920
9032
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
8921
9033
|
*
|
|
8922
9034
|
* @example
|
|
@@ -8966,6 +9078,8 @@ var IdentityClient = class {
|
|
|
8966
9078
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
8967
9079
|
case 403:
|
|
8968
9080
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
9081
|
+
case 409:
|
|
9082
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
8969
9083
|
case 429:
|
|
8970
9084
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
8971
9085
|
default:
|
|
@@ -9034,13 +9148,14 @@ var IdentityClient = class {
|
|
|
9034
9148
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/users/{id}");
|
|
9035
9149
|
}
|
|
9036
9150
|
/**
|
|
9037
|
-
* 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.
|
|
9151
|
+
* 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.
|
|
9038
9152
|
*
|
|
9039
9153
|
* @param {Vectros.UpdateUserRequest} request
|
|
9040
9154
|
* @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
9041
9155
|
*
|
|
9042
9156
|
* @throws {@link Vectros.BadRequestError}
|
|
9043
9157
|
* @throws {@link Vectros.NotFoundError}
|
|
9158
|
+
* @throws {@link Vectros.ConflictError}
|
|
9044
9159
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
9045
9160
|
*
|
|
9046
9161
|
* @example
|
|
@@ -9088,6 +9203,8 @@ var IdentityClient = class {
|
|
|
9088
9203
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
9089
9204
|
case 404:
|
|
9090
9205
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
9206
|
+
case 409:
|
|
9207
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
9091
9208
|
case 429:
|
|
9092
9209
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9093
9210
|
default:
|
|
@@ -9349,7 +9466,7 @@ var InferenceClient = class {
|
|
|
9349
9466
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
9350
9467
|
}
|
|
9351
9468
|
/**
|
|
9352
|
-
* Returns
|
|
9469
|
+
* 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`.
|
|
9353
9470
|
*
|
|
9354
9471
|
* @param {InferenceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
9355
9472
|
*
|
|
@@ -9393,7 +9510,7 @@ var InferenceClient = class {
|
|
|
9393
9510
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/models");
|
|
9394
9511
|
}
|
|
9395
9512
|
/**
|
|
9396
|
-
* 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.
|
|
9513
|
+
* 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.
|
|
9397
9514
|
*/
|
|
9398
9515
|
chatInference(request, requestOptions) {
|
|
9399
9516
|
return HttpResponsePromise.fromPromise(this.__chatInference(request, requestOptions));
|
|
@@ -9446,8 +9563,14 @@ var InferenceClient = class {
|
|
|
9446
9563
|
throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
|
|
9447
9564
|
case 403:
|
|
9448
9565
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
9566
|
+
case 404:
|
|
9567
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
9568
|
+
case 413:
|
|
9569
|
+
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
9449
9570
|
case 429:
|
|
9450
9571
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9572
|
+
case 503:
|
|
9573
|
+
throw new ServiceUnavailableError(_response.error.body, _response.rawResponse);
|
|
9451
9574
|
default:
|
|
9452
9575
|
throw new VectrosError({
|
|
9453
9576
|
statusCode: _response.error.statusCode,
|
|
@@ -9521,6 +9644,8 @@ var InferenceClient = class {
|
|
|
9521
9644
|
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
9522
9645
|
case 429:
|
|
9523
9646
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9647
|
+
case 503:
|
|
9648
|
+
throw new ServiceUnavailableError(_response.error.body, _response.rawResponse);
|
|
9524
9649
|
default:
|
|
9525
9650
|
throw new VectrosError({
|
|
9526
9651
|
statusCode: _response.error.statusCode,
|
|
@@ -9585,10 +9710,16 @@ var InferenceClient = class {
|
|
|
9585
9710
|
throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
|
|
9586
9711
|
case 403:
|
|
9587
9712
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
9713
|
+
case 404:
|
|
9714
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
9715
|
+
case 413:
|
|
9716
|
+
throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
|
|
9588
9717
|
case 429:
|
|
9589
9718
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9590
9719
|
case 500:
|
|
9591
9720
|
throw new InternalServerError(_response.error.body, _response.rawResponse);
|
|
9721
|
+
case 503:
|
|
9722
|
+
throw new ServiceUnavailableError(_response.error.body, _response.rawResponse);
|
|
9592
9723
|
default:
|
|
9593
9724
|
throw new VectrosError({
|
|
9594
9725
|
statusCode: _response.error.statusCode,
|
|
@@ -9666,7 +9797,7 @@ var RecordsClient = class {
|
|
|
9666
9797
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch-get");
|
|
9667
9798
|
}
|
|
9668
9799
|
/**
|
|
9669
|
-
* Reserved endpoint for batch lookup and reference resolution. The published response shape correlates results to each input and carries a per-item status envelope.
|
|
9800
|
+
* 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.
|
|
9670
9801
|
*
|
|
9671
9802
|
* @param {Vectros.BatchLookupRequest} request
|
|
9672
9803
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -9727,7 +9858,7 @@ var RecordsClient = class {
|
|
|
9727
9858
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/lookup/batch");
|
|
9728
9859
|
}
|
|
9729
9860
|
/**
|
|
9730
|
-
* Writes up to 50 records
|
|
9861
|
+
* 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.
|
|
9731
9862
|
*
|
|
9732
9863
|
* `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.
|
|
9733
9864
|
*
|
|
@@ -9866,6 +9997,8 @@ var RecordsClient = class {
|
|
|
9866
9997
|
*
|
|
9867
9998
|
* @throws {@link Vectros.BadRequestError}
|
|
9868
9999
|
* @throws {@link Vectros.ForbiddenError}
|
|
10000
|
+
* @throws {@link Vectros.NotFoundError}
|
|
10001
|
+
* @throws {@link Vectros.ConflictError}
|
|
9869
10002
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
9870
10003
|
*
|
|
9871
10004
|
* @example
|
|
@@ -9922,6 +10055,10 @@ var RecordsClient = class {
|
|
|
9922
10055
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
9923
10056
|
case 403:
|
|
9924
10057
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
10058
|
+
case 404:
|
|
10059
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
10060
|
+
case 409:
|
|
10061
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
9925
10062
|
case 429:
|
|
9926
10063
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
9927
10064
|
default:
|
|
@@ -10070,6 +10207,7 @@ var RecordsClient = class {
|
|
|
10070
10207
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
10071
10208
|
*
|
|
10072
10209
|
* @throws {@link Vectros.NotFoundError}
|
|
10210
|
+
* @throws {@link Vectros.ConflictError}
|
|
10073
10211
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
10074
10212
|
*
|
|
10075
10213
|
* @example
|
|
@@ -10109,6 +10247,8 @@ var RecordsClient = class {
|
|
|
10109
10247
|
switch (_response.error.statusCode) {
|
|
10110
10248
|
case 404:
|
|
10111
10249
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
10250
|
+
case 409:
|
|
10251
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
10112
10252
|
case 429:
|
|
10113
10253
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
10114
10254
|
default:
|
|
@@ -10122,7 +10262,7 @@ var RecordsClient = class {
|
|
|
10122
10262
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/records/{id}");
|
|
10123
10263
|
}
|
|
10124
10264
|
/**
|
|
10125
|
-
* 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
|
|
10265
|
+
* 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.
|
|
10126
10266
|
*
|
|
10127
10267
|
* @param {Vectros.PatchRecordRequest} request
|
|
10128
10268
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -10551,6 +10691,8 @@ var SchemasClient = class {
|
|
|
10551
10691
|
*
|
|
10552
10692
|
* @throws {@link Vectros.BadRequestError}
|
|
10553
10693
|
* @throws {@link Vectros.ForbiddenError}
|
|
10694
|
+
* @throws {@link Vectros.NotFoundError}
|
|
10695
|
+
* @throws {@link Vectros.ConflictError}
|
|
10554
10696
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
10555
10697
|
*
|
|
10556
10698
|
* @example
|
|
@@ -10620,6 +10762,10 @@ var SchemasClient = class {
|
|
|
10620
10762
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
10621
10763
|
case 403:
|
|
10622
10764
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
10765
|
+
case 404:
|
|
10766
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
10767
|
+
case 409:
|
|
10768
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
10623
10769
|
case 429:
|
|
10624
10770
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
10625
10771
|
default:
|
|
@@ -10763,7 +10909,7 @@ var SchemasClient = class {
|
|
|
10763
10909
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/v1/schemas/{id}");
|
|
10764
10910
|
}
|
|
10765
10911
|
/**
|
|
10766
|
-
* 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.
|
|
10912
|
+
* 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.
|
|
10767
10913
|
*
|
|
10768
10914
|
* @param {Vectros.DeleteSchemaRequest} request
|
|
10769
10915
|
* @param {SchemasClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -10890,7 +11036,7 @@ var ScriptsClient = class {
|
|
|
10890
11036
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
10891
11037
|
}
|
|
10892
11038
|
/**
|
|
10893
|
-
* 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
|
|
11039
|
+
* 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.
|
|
10894
11040
|
*
|
|
10895
11041
|
* **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.
|
|
10896
11042
|
*
|
|
@@ -11504,6 +11650,8 @@ var TriggersClient = class {
|
|
|
11504
11650
|
*
|
|
11505
11651
|
* @throws {@link Vectros.BadRequestError}
|
|
11506
11652
|
* @throws {@link Vectros.ForbiddenError}
|
|
11653
|
+
* @throws {@link Vectros.NotFoundError}
|
|
11654
|
+
* @throws {@link Vectros.ConflictError}
|
|
11507
11655
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
11508
11656
|
*
|
|
11509
11657
|
* @example
|
|
@@ -11561,6 +11709,10 @@ var TriggersClient = class {
|
|
|
11561
11709
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
11562
11710
|
case 403:
|
|
11563
11711
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
11712
|
+
case 404:
|
|
11713
|
+
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
11714
|
+
case 409:
|
|
11715
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
11564
11716
|
case 429:
|
|
11565
11717
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
11566
11718
|
default:
|
|
@@ -11637,6 +11789,7 @@ var TriggersClient = class {
|
|
|
11637
11789
|
* @throws {@link Vectros.BadRequestError}
|
|
11638
11790
|
* @throws {@link Vectros.ForbiddenError}
|
|
11639
11791
|
* @throws {@link Vectros.NotFoundError}
|
|
11792
|
+
* @throws {@link Vectros.ConflictError}
|
|
11640
11793
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
11641
11794
|
*
|
|
11642
11795
|
* @example
|
|
@@ -11694,6 +11847,8 @@ var TriggersClient = class {
|
|
|
11694
11847
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
11695
11848
|
case 404:
|
|
11696
11849
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
11850
|
+
case 409:
|
|
11851
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
11697
11852
|
case 429:
|
|
11698
11853
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
11699
11854
|
default:
|
|
@@ -11713,6 +11868,7 @@ var TriggersClient = class {
|
|
|
11713
11868
|
* @param {TriggersClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
11714
11869
|
*
|
|
11715
11870
|
* @throws {@link Vectros.NotFoundError}
|
|
11871
|
+
* @throws {@link Vectros.ConflictError}
|
|
11716
11872
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
11717
11873
|
*
|
|
11718
11874
|
* @example
|
|
@@ -11752,6 +11908,8 @@ var TriggersClient = class {
|
|
|
11752
11908
|
switch (_response.error.statusCode) {
|
|
11753
11909
|
case 404:
|
|
11754
11910
|
throw new NotFoundError(_response.error.body, _response.rawResponse);
|
|
11911
|
+
case 409:
|
|
11912
|
+
throw new ConflictError(_response.error.body, _response.rawResponse);
|
|
11755
11913
|
case 429:
|
|
11756
11914
|
throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
|
|
11757
11915
|
default:
|
|
@@ -11882,7 +12040,7 @@ var RenderHintsSchema = z.object({
|
|
|
11882
12040
|
helpText: z.string().optional(),
|
|
11883
12041
|
// Marks this field as the record's headline (display) field — the linked
|
|
11884
12042
|
// primary column in the records list + the title on the detail view. At most
|
|
11885
|
-
// one per schema (the platform
|
|
12043
|
+
// one per schema (the platform stores the hint and does not enforce one; the reference UI uses the first by order). Format passthrough →
|
|
11886
12044
|
// SchemaRequest.renderHints[fieldId].displayField.
|
|
11887
12045
|
displayField: z.boolean().optional()
|
|
11888
12046
|
}).strict();
|
|
@@ -12087,7 +12245,7 @@ var BlueprintSchemaSchema = z.object({
|
|
|
12087
12245
|
// (`lintNamespacedScopeArrays`) — this `.max(MAX_SCOPE_DIMENSIONS)` is
|
|
12088
12246
|
// only the shape-level cap, not the grammar check.
|
|
12089
12247
|
// With a scoped token these must be consistent with the profile's dataScope
|
|
12090
|
-
// (
|
|
12248
|
+
// (the lints here check each entry's grammar only, not that consistency).
|
|
12091
12249
|
userId: z.string().min(1).optional(),
|
|
12092
12250
|
scopes: z.array(z.string().min(1)).max(MAX_SCOPE_DIMENSIONS).optional(),
|
|
12093
12251
|
// The id of an existing schema this one is a CUSTOMIZATION of, when a schema
|
|
@@ -12453,8 +12611,8 @@ var IdentityDeclSchema = z.object({
|
|
|
12453
12611
|
kind: IdentityKindSchema,
|
|
12454
12612
|
// safeDisplayString: `@vectros-ai/cli`'s `runBootstrapCli` and `blueprint plan` both print
|
|
12455
12613
|
// this RAW to stdout ("~ <name> (<kind>) — external id '<externalId>'"), immediately before
|
|
12456
|
-
// the "Apply this plan? [y/N]" confirmation gate itself — the highest-severity
|
|
12457
|
-
//
|
|
12614
|
+
// the "Apply this plan? [y/N]" confirmation gate itself — the highest-severity place to
|
|
12615
|
+
// close, since it sits at the confirmation prompt's own disclosure text, not merely
|
|
12458
12616
|
// somewhere upstream of it. `displayName` is wrapped too though not currently
|
|
12459
12617
|
// rendered in any preview (defense-in-depth, matching this file's practice for its adjacent
|
|
12460
12618
|
// sibling fields) — it does reach a platform API payload as an entity's `name`.
|
|
@@ -12800,7 +12958,7 @@ var SeedCommonShape = {
|
|
|
12800
12958
|
* seeds resolve a `reference` against (across surfaces: a record seed may
|
|
12801
12959
|
* reference a document seed by its externalId, and vice versa).
|
|
12802
12960
|
* `safeDisplayString`: not currently rendered in a plan-preview line (same as this schema's
|
|
12803
|
-
* `typeName` just above
|
|
12961
|
+
* `typeName` just above), but the same "costs nothing to close" standard
|
|
12804
12962
|
* applies — a short caller-supplied id, not free-form prose, so no YAML block-scalar
|
|
12805
12963
|
* collision risk like `description`'s.
|
|
12806
12964
|
*/
|
|
@@ -13465,10 +13623,10 @@ var BlueprintSchema = z.object({
|
|
|
13465
13623
|
/** Stable blueprint id (the `--blueprint <name>` selector + idempotency key). */
|
|
13466
13624
|
name: safeDisplayString(z.string().min(1)),
|
|
13467
13625
|
version: safeDisplayString(z.string().min(1)),
|
|
13468
|
-
// NOT safeDisplayString — deliberately
|
|
13469
|
-
// blueprint
|
|
13626
|
+
// NOT safeDisplayString — deliberately: that wrap breaks real, multi-line-authored
|
|
13627
|
+
// blueprint `description`s. `description` is never interpolated into any
|
|
13470
13628
|
// rendered plan-preview line (`buildPlan`'s header prints
|
|
13471
|
-
// `name`/`version`/`contextId`, never `description`
|
|
13629
|
+
// `name`/`version`/`contextId`, never `description`), so
|
|
13472
13630
|
// wrapping it protected a sink that does not exist. It also actively broke real content:
|
|
13473
13631
|
// this is exactly the field authors reach for a YAML folded (`>`) or literal (`|`) block
|
|
13474
13632
|
// scalar to write, and clip chomping (the YAML default) always leaves exactly one trailing
|
|
@@ -13483,9 +13641,8 @@ var BlueprintSchema = z.object({
|
|
|
13483
13641
|
/**
|
|
13484
13642
|
* Human-readable app-context name; defaults to `MCP — <name>` (see {@link contextNameOf})
|
|
13485
13643
|
* when absent. `safeDisplayString`: `contextNameOf` renders this raw into the FIRST line of
|
|
13486
|
-
* every `buildPlan` summary (the `context '...' (...)` step)
|
|
13487
|
-
*
|
|
13488
|
-
* `name`/`version`/`description` right above it.
|
|
13644
|
+
* every `buildPlan` summary (the `context '...' (...)` step); same shape as
|
|
13645
|
+
* `name`/`version` right above it.
|
|
13489
13646
|
*/
|
|
13490
13647
|
contextName: safeDisplayString(z.string().min(1)).optional(),
|
|
13491
13648
|
/**
|
|
@@ -13541,7 +13698,8 @@ var BlueprintSchema = z.object({
|
|
|
13541
13698
|
* alongside app-context/service-principal creation and issuers, BEFORE
|
|
13542
13699
|
* schemas/seed (an `entityBacked` namespace's entities, and an
|
|
13543
13700
|
* `allowedSurfaces: ['entity']` schema, both need the registration to
|
|
13544
|
-
* already exist). `org`/`client`
|
|
13701
|
+
* already exist). `org`/`client` are registered the same way as any other
|
|
13702
|
+
* namespace: signup does not register them, so declare an entry for each you use.
|
|
13545
13703
|
*/
|
|
13546
13704
|
namespaces: z.array(BlueprintNamespaceSchema).optional(),
|
|
13547
13705
|
/**
|
|
@@ -13579,9 +13737,8 @@ var BlueprintSchema = z.object({
|
|
|
13579
13737
|
*/
|
|
13580
13738
|
identityProjectionClaims: z.array(
|
|
13581
13739
|
// safeDisplayString: rendered raw into buildPlan's context step (`identity projection:
|
|
13582
|
-
// [...]`) — same rendered-plan-preview shape as `capturedClaims` (its structural pair
|
|
13583
|
-
//
|
|
13584
|
-
// applies (safeDisplayString's own `.refine` before the blank-name `.refine` below,
|
|
13740
|
+
// [...]`) — same rendered-plan-preview shape as `capturedClaims` (its structural pair);
|
|
13741
|
+
// the same ordering rule applies (safeDisplayString's own `.refine` before the blank-name `.refine` below,
|
|
13585
13742
|
// both after `.max` since `.refine` returns a ZodEffects with no `.max`).
|
|
13586
13743
|
safeDisplayString(
|
|
13587
13744
|
z.string().max(MAX_CAPTURED_CLAIM_NAME_LENGTH, {
|
|
@@ -13786,7 +13943,7 @@ function deriveSuffix(contextId) {
|
|
|
13786
13943
|
var WHOLE_TOKEN_RE = /^\$\{\{\s*([A-Za-z_]\w*)\.([A-Za-z_]\w*)\s*\}\}$/;
|
|
13787
13944
|
var TOKEN_SCAN_RE = /\$\$\{\{|\$\{\{([\s\S]*?)\}\}|\$\{\{/g;
|
|
13788
13945
|
var INNER_REF_RE = /^([A-Za-z_]\w*)\.([A-Za-z_]\w*)$/;
|
|
13789
|
-
var DEFERRED_NAMESPACES = /* @__PURE__ */ new Set(["self", "identities", "under", "member"]);
|
|
13946
|
+
var DEFERRED_NAMESPACES = /* @__PURE__ */ new Set(["self", "identities", "under", "member", "input"]);
|
|
13790
13947
|
var DEFERRED_BARE_TOKENS = /* @__PURE__ */ new Set(["any"]);
|
|
13791
13948
|
var DEFERRED_NS_ALTERNATION = [...DEFERRED_NAMESPACES].join("|");
|
|
13792
13949
|
var DEFERRED_INNER_RE = new RegExp(
|
|
@@ -16355,14 +16512,14 @@ var COGNITO_ALLOWED_HOSTS = Object.values(PER_ENV).map(
|
|
|
16355
16512
|
);
|
|
16356
16513
|
var BAKED_CLIENT_IDS = {
|
|
16357
16514
|
staging: "61hjvm25kkjb1eei8t2d3jkcl8",
|
|
16358
|
-
//
|
|
16515
|
+
// deployed staging hosted UI client
|
|
16359
16516
|
production: "63eipeedjv78q653k45niih46f"
|
|
16360
|
-
//
|
|
16517
|
+
// deployed production hosted UI client
|
|
16361
16518
|
};
|
|
16362
16519
|
var MissingCognitoClientIdError = class extends Error {
|
|
16363
16520
|
constructor(env) {
|
|
16364
16521
|
super(
|
|
16365
|
-
`No Cognito client id for '${env}'. Set VECTROS_COGNITO_CLIENT_ID
|
|
16522
|
+
`No Cognito client id for '${env}'. Set VECTROS_COGNITO_CLIENT_ID to the public client id of the sign-in app.`
|
|
16366
16523
|
);
|
|
16367
16524
|
this.name = "MissingCognitoClientIdError";
|
|
16368
16525
|
}
|
|
@@ -16505,6 +16662,25 @@ async function clearSession(baseDir) {
|
|
|
16505
16662
|
return false;
|
|
16506
16663
|
}
|
|
16507
16664
|
}
|
|
16665
|
+
var TokenEndpointError = class extends Error {
|
|
16666
|
+
status;
|
|
16667
|
+
/** The OAuth `error` field of a JSON error body (e.g. `invalid_grant`), when there is one. */
|
|
16668
|
+
oauthError;
|
|
16669
|
+
constructor(status, text) {
|
|
16670
|
+
super(`token endpoint HTTP ${status}${text ? ` \u2014 ${stripControlCharacters(text, 500)}` : ""}`);
|
|
16671
|
+
this.name = "TokenEndpointError";
|
|
16672
|
+
this.status = status;
|
|
16673
|
+
this.oauthError = oauthErrorOf(text);
|
|
16674
|
+
}
|
|
16675
|
+
};
|
|
16676
|
+
function oauthErrorOf(text) {
|
|
16677
|
+
try {
|
|
16678
|
+
const parsed = JSON.parse(text);
|
|
16679
|
+
return typeof parsed.error === "string" ? parsed.error : void 0;
|
|
16680
|
+
} catch {
|
|
16681
|
+
return void 0;
|
|
16682
|
+
}
|
|
16683
|
+
}
|
|
16508
16684
|
async function postToken(config, body, fetchImpl) {
|
|
16509
16685
|
const res = await fetchImpl(tokenEndpoint(config), {
|
|
16510
16686
|
method: "POST",
|
|
@@ -16512,9 +16688,7 @@ async function postToken(config, body, fetchImpl) {
|
|
|
16512
16688
|
body: body.toString()
|
|
16513
16689
|
});
|
|
16514
16690
|
const text = await res.text().catch(() => "");
|
|
16515
|
-
if (!res.ok)
|
|
16516
|
-
throw new Error(`token endpoint HTTP ${res.status}${text ? ` \u2014 ${stripControlCharacters(text, 500)}` : ""}`);
|
|
16517
|
-
}
|
|
16691
|
+
if (!res.ok) throw new TokenEndpointError(res.status, text);
|
|
16518
16692
|
try {
|
|
16519
16693
|
return JSON.parse(text);
|
|
16520
16694
|
} catch {
|
|
@@ -16530,32 +16704,56 @@ async function refreshTokens(config, refreshToken, fetchImpl = fetch) {
|
|
|
16530
16704
|
|
|
16531
16705
|
// src/auth-context.ts
|
|
16532
16706
|
var COGNITO_TOKEN_ENV = "VECTROS_BOOTSTRAP_TOKEN";
|
|
16707
|
+
function loginCommandFor(env) {
|
|
16708
|
+
return env === "staging" ? "vectros login --env staging" : "vectros login";
|
|
16709
|
+
}
|
|
16533
16710
|
var MissingCredentialError = class extends Error {
|
|
16534
|
-
constructor() {
|
|
16711
|
+
constructor(env) {
|
|
16535
16712
|
super(
|
|
16536
|
-
`No credential found.
|
|
16713
|
+
`No credential found. Run \`${loginCommandFor(env)}\` to sign in, or set ${COGNITO_TOKEN_ENV} or pass --token (generate a token in the developer portal).`
|
|
16537
16714
|
);
|
|
16538
16715
|
this.name = "MissingCredentialError";
|
|
16539
16716
|
}
|
|
16540
16717
|
};
|
|
16718
|
+
var SessionExpiredError = class extends Error {
|
|
16719
|
+
constructor(env) {
|
|
16720
|
+
super(
|
|
16721
|
+
`Your Vectros login session has expired or was revoked. Run \`${loginCommandFor(env)}\` to sign in again.`
|
|
16722
|
+
);
|
|
16723
|
+
this.name = "SessionExpiredError";
|
|
16724
|
+
}
|
|
16725
|
+
};
|
|
16541
16726
|
function resolveCognitoTokenSync(explicit) {
|
|
16542
16727
|
if (explicit && explicit.trim()) return explicit.trim();
|
|
16543
16728
|
const fromEnv = process.env[COGNITO_TOKEN_ENV];
|
|
16544
16729
|
if (fromEnv && fromEnv.trim()) return fromEnv.trim();
|
|
16545
16730
|
return void 0;
|
|
16546
16731
|
}
|
|
16547
|
-
async function resolveCognitoToken(env, explicit) {
|
|
16732
|
+
async function resolveCognitoToken(env, explicit, deps = { readSession, refreshTokens, saveSession }) {
|
|
16548
16733
|
const sync = resolveCognitoTokenSync(explicit);
|
|
16549
16734
|
if (sync) return sync;
|
|
16550
|
-
const session = await readSession();
|
|
16735
|
+
const session = await deps.readSession();
|
|
16551
16736
|
if (session && session.env === env) {
|
|
16552
16737
|
const config = resolveCognitoConfig(env);
|
|
16553
|
-
|
|
16738
|
+
let tokens;
|
|
16739
|
+
try {
|
|
16740
|
+
tokens = await deps.refreshTokens(config, session.refreshToken);
|
|
16741
|
+
} catch (err) {
|
|
16742
|
+
if (err instanceof TokenEndpointError && err.oauthError === "invalid_grant") throw new SessionExpiredError(env);
|
|
16743
|
+
throw err;
|
|
16744
|
+
}
|
|
16745
|
+
if (tokens.refresh_token && tokens.refresh_token !== session.refreshToken) {
|
|
16746
|
+
const stored = await deps.readSession().catch(() => void 0);
|
|
16747
|
+
if (stored?.refreshToken === session.refreshToken) {
|
|
16748
|
+
await deps.saveSession({ ...session, refreshToken: tokens.refresh_token }).catch(() => void 0);
|
|
16749
|
+
}
|
|
16750
|
+
}
|
|
16554
16751
|
const bearer = tokens.id_token ?? tokens.access_token;
|
|
16555
16752
|
if (bearer) return bearer;
|
|
16556
16753
|
}
|
|
16557
|
-
throw new MissingCredentialError();
|
|
16754
|
+
throw new MissingCredentialError(env);
|
|
16558
16755
|
}
|
|
16756
|
+
var DEFAULT_CONTEXT_ID = "default";
|
|
16559
16757
|
async function resolveAuthContext(opts) {
|
|
16560
16758
|
const baseUrl = envToBaseUrl(opts.env, opts.baseUrl ?? process.env.VECTROS_API_BASE_URL);
|
|
16561
16759
|
const cognitoToken = await resolveCognitoToken(opts.env, opts.token);
|
|
@@ -16565,7 +16763,7 @@ async function resolveAuthContext(opts) {
|
|
|
16565
16763
|
apiBaseUrl: baseUrl,
|
|
16566
16764
|
tenant,
|
|
16567
16765
|
...opts.context ? { contextId: opts.context } : {}
|
|
16568
|
-
}) : await mintContextToken({ cognitoToken, apiBaseUrl: baseUrl, contextId: opts.context ??
|
|
16766
|
+
}) : await mintContextToken({ cognitoToken, apiBaseUrl: baseUrl, contextId: opts.context ?? DEFAULT_CONTEXT_ID, tenant });
|
|
16569
16767
|
const client = new VectrosClient({ token: bridge.token, environment: baseUrl });
|
|
16570
16768
|
const identity = await resolveIdentity({ log: opts.log, apiKey: bridge.token, environment: baseUrl });
|
|
16571
16769
|
return {
|
|
@@ -17066,6 +17264,7 @@ function keyNameFor(pack, machineId) {
|
|
|
17066
17264
|
}
|
|
17067
17265
|
|
|
17068
17266
|
// src/identity-resolver.ts
|
|
17267
|
+
var IDENTITIES_TO_ENSURE_HEADING = " Identities to ensure (idempotent by external id; a non-user entity lives with its namespace):";
|
|
17069
17268
|
function userPayload(decl) {
|
|
17070
17269
|
const meta = decl.metadata;
|
|
17071
17270
|
if (decl.displayName === void 0) return meta;
|
|
@@ -17206,7 +17405,7 @@ async function withAuth(opts, deps, body) {
|
|
|
17206
17405
|
log
|
|
17207
17406
|
});
|
|
17208
17407
|
} catch (err) {
|
|
17209
|
-
if (err instanceof MissingCredentialError || err instanceof InvalidBaseUrlError || err instanceof InvalidCognitoRegionError) {
|
|
17408
|
+
if (err instanceof MissingCredentialError || err instanceof SessionExpiredError || err instanceof InvalidBaseUrlError || err instanceof InvalidCognitoRegionError) {
|
|
17210
17409
|
process.stderr.write(` \u2716 ${err.message}
|
|
17211
17410
|
`);
|
|
17212
17411
|
return 2;
|
|
@@ -17673,13 +17872,11 @@ function toSdkFields(blueprint, schemaIndex) {
|
|
|
17673
17872
|
searchable: f.searchable,
|
|
17674
17873
|
filterable: f.filterable,
|
|
17675
17874
|
// Forwarded explicitly, like every sibling here: this mapping is a named-property list, not a
|
|
17676
|
-
// spread, so an unforwarded field is dropped SILENTLY rather than failing
|
|
17677
|
-
// happened to `validation` before it was added below.
|
|
17875
|
+
// spread, so an unforwarded field is dropped SILENTLY rather than failing.
|
|
17678
17876
|
inline: f.inline,
|
|
17679
17877
|
description: f.description,
|
|
17680
17878
|
enumValues: mapEnumValues(f.enumValues),
|
|
17681
|
-
// Format passthrough
|
|
17682
|
-
// "smoking gun" toSdkFields mapped only six props). The platform
|
|
17879
|
+
// Format passthrough. The platform
|
|
17683
17880
|
// `SchemaRequest.FieldDef.validation` enforces these server-side. The
|
|
17684
17881
|
// typed blueprint ValidationRules narrows the SDK's free-form Record.
|
|
17685
17882
|
validation: f.validation,
|
|
@@ -17801,21 +17998,12 @@ async function provisionContext(client, input) {
|
|
|
17801
17998
|
const { blueprint, allowTenantWideNamespaces = false, readExistingKey, confirmExistingPrincipal, log } = input;
|
|
17802
17999
|
assertBlueprintGates(blueprint);
|
|
17803
18000
|
assertTenantWideNamespacesAllowed(blueprint, allowTenantWideNamespaces);
|
|
17804
|
-
const ctxResp = await client.auth.createAppContext({
|
|
17805
|
-
body: {
|
|
17806
|
-
contextId: blueprint.contextId,
|
|
17807
|
-
name: contextNameOf(blueprint),
|
|
17808
|
-
...blueprint.identityProjectionClaims === void 0 ? {} : { identityProjectionClaims: blueprint.identityProjectionClaims }
|
|
17809
|
-
}
|
|
17810
|
-
});
|
|
17811
|
-
const contextAction = actionOf(ctxResp.created);
|
|
17812
|
-
log.debug({ component: "loader", contextId: blueprint.contextId, contextAction }, "app-context ensured");
|
|
17813
18001
|
const user = await client.identity.createUser({
|
|
17814
18002
|
body: {
|
|
17815
18003
|
externalId: blueprint.servicePrincipal.externalId,
|
|
17816
18004
|
type: "SERVICE",
|
|
17817
18005
|
// SDK 0.18+ (identity-access rework): UserRequest's free-form attribute bag
|
|
17818
|
-
//
|
|
18006
|
+
// is `payload` (arbitrary key/value).
|
|
17819
18007
|
payload: { displayName: blueprint.servicePrincipal.displayName }
|
|
17820
18008
|
}
|
|
17821
18009
|
});
|
|
@@ -17850,6 +18038,15 @@ async function provisionContext(client, input) {
|
|
|
17850
18038
|
"existing-principal collision confirmed (or unwired) \u2014 proceeding with any further write to this principal"
|
|
17851
18039
|
);
|
|
17852
18040
|
}
|
|
18041
|
+
const ctxResp = await client.auth.createAppContext({
|
|
18042
|
+
body: {
|
|
18043
|
+
contextId: blueprint.contextId,
|
|
18044
|
+
name: contextNameOf(blueprint),
|
|
18045
|
+
...blueprint.identityProjectionClaims === void 0 ? {} : { identityProjectionClaims: blueprint.identityProjectionClaims }
|
|
18046
|
+
}
|
|
18047
|
+
});
|
|
18048
|
+
const contextAction = actionOf(ctxResp.created);
|
|
18049
|
+
log.debug({ component: "loader", contextId: blueprint.contextId, contextAction }, "app-context ensured");
|
|
17853
18050
|
const needsContextPinnedBootstrap = (blueprint.namespaces ?? []).length > 0;
|
|
17854
18051
|
const ctxBootstrapClient = needsContextPinnedBootstrap ? input.remintBootstrapForContext ? await input.remintBootstrapForContext(blueprint.contextId) : client : void 0;
|
|
17855
18052
|
if (ctxBootstrapClient && input.remintBootstrapForContext) {
|
|
@@ -17980,7 +18177,7 @@ async function runLoader(client, input) {
|
|
|
17980
18177
|
// them), so default to ['record'] when a blueprint omits it. The `as` keeps
|
|
17981
18178
|
// the literal default assignable to the SDK's enum-array type.
|
|
17982
18179
|
allowedSurfaces: s.allowedSurfaces ?? ["record"],
|
|
17983
|
-
// Format passthrough
|
|
18180
|
+
// Format passthrough. Ownership mirrors
|
|
17984
18181
|
// the SDK `SchemaRequest` shape 1:1: the principal `userId` plus `scopes`
|
|
17985
18182
|
// (namespaced parent edges `<ns>:<value>`; the flat orgId/clientId fields are gone).
|
|
17986
18183
|
renderHints: toRenderHintMap(s),
|
|
@@ -18358,14 +18555,14 @@ function renderPlanDataScope(ds) {
|
|
|
18358
18555
|
function buildPlan(blueprint, opts = {}) {
|
|
18359
18556
|
const steps = [];
|
|
18360
18557
|
const projection = blueprint.identityProjectionClaims?.length ? `, identity projection: [${blueprint.identityProjectionClaims.join(", ")}]` : blueprint.identityProjectionClaims ? ", identity projection: disabled (empty list)" : "";
|
|
18361
|
-
steps.push({
|
|
18362
|
-
kind: "context",
|
|
18363
|
-
summary: `context '${blueprint.contextId}' (${contextNameOf(blueprint)}${projection})`
|
|
18364
|
-
});
|
|
18365
18558
|
steps.push({
|
|
18366
18559
|
kind: "principal",
|
|
18367
18560
|
summary: `principal '${blueprint.servicePrincipal.externalId}' (SERVICE)`
|
|
18368
18561
|
});
|
|
18562
|
+
steps.push({
|
|
18563
|
+
kind: "context",
|
|
18564
|
+
summary: `context '${blueprint.contextId}' (${contextNameOf(blueprint)}${projection})`
|
|
18565
|
+
});
|
|
18369
18566
|
for (const ns of blueprint.namespaces ?? []) {
|
|
18370
18567
|
const ownerNote = ns.tenantWide ? "TENANT-WIDE \u2014 visible to every context" : `owned by '${blueprint.contextId}'`;
|
|
18371
18568
|
const entityNote = ns.entityBacked ? ", entity-backed" : "";
|
|
@@ -18831,10 +19028,8 @@ var OsKeychainSecretStore = class {
|
|
|
18831
19028
|
}
|
|
18832
19029
|
/**
|
|
18833
19030
|
* `present` or `unknown` — **never `absent`**: impossible through `getPassword`,
|
|
18834
|
-
* and held there by POLICY where other evidence exists
|
|
18835
|
-
*
|
|
18836
|
-
* tighten later", which the rest of this very docstring then disowns — the
|
|
18837
|
-
* widening procedure is spelled out below. State the real shape up front.)
|
|
19031
|
+
* and held there by POLICY where other evidence exists (the widening procedure
|
|
19032
|
+
* is spelled out below).
|
|
18838
19033
|
*
|
|
18839
19034
|
* Proving absence would require distinguishing "no such entry" from "I could not
|
|
18840
19035
|
* read it", and that information does not survive the binding: `get_password` is
|
|
@@ -18854,7 +19049,7 @@ var OsKeychainSecretStore = class {
|
|
|
18854
19049
|
* Linux collection `secret-tool` exits 1 while `getPassword` returns `null`, so
|
|
18855
19050
|
* the D-Bus layer reports the fault faithfully and `.ok()` is what discards it.
|
|
18856
19051
|
* A read with real error semantics could therefore make `absent` reachable — and
|
|
18857
|
-
* `find_credentials`
|
|
19052
|
+
* `find_credentials` HAS verified semantics on Linux: it throws
|
|
18858
19053
|
* `org.freedesktop.Secret.Error.IsLocked` against a locked collection (measured;
|
|
18859
19054
|
* see {@link OsKeychainSecretStore.deleteSecret} for the transcript). That is why
|
|
18860
19055
|
* `deleteSecret` may confirm a delete while this method may not.
|
|
@@ -18864,7 +19059,7 @@ var OsKeychainSecretStore = class {
|
|
|
18864
19059
|
* PRUNABLE, which is a behaviour change this CLI has not taken, and Windows'
|
|
18865
19060
|
* locked-store behaviour is unmeasured (it has no comparable lock state, so there
|
|
18866
19061
|
* is nothing yet to observe). Verify per-platform before widening; do not infer it
|
|
18867
|
-
* from a signature
|
|
19062
|
+
* from a signature.
|
|
18868
19063
|
*/
|
|
18869
19064
|
async presence(ref) {
|
|
18870
19065
|
try {
|
|
@@ -18895,10 +19090,8 @@ var OsKeychainSecretStore = class {
|
|
|
18895
19090
|
*
|
|
18896
19091
|
* MEASURED, not inferred — and the distinction is the whole point, because
|
|
18897
19092
|
* {@link OsKeychainSecretStore.presence} rightly forbids inferring a locked
|
|
18898
|
-
* store's behaviour from a `Result` signature
|
|
18899
|
-
*
|
|
18900
|
-
* precisely that, on the path that authorizes destruction. Being right by luck is
|
|
18901
|
-
* not being right. Against a real locked collection (`node:24-alpine`, non-root,
|
|
19093
|
+
* store's behaviour from a `Result` signature, and doing so on the path that
|
|
19094
|
+
* authorizes destruction would be right only by luck. Against a real locked collection (`node:24-alpine`, non-root,
|
|
18902
19095
|
* `dbus-run-session` + gnome-keyring, collection locked via
|
|
18903
19096
|
* `org.freedesktop.Secret.Service.Lock`):
|
|
18904
19097
|
*
|
|
@@ -18913,16 +19106,14 @@ var OsKeychainSecretStore = class {
|
|
|
18913
19106
|
* cached handle to it. CI cannot pin this half — it unlocks the collection so the rest of
|
|
18914
19107
|
* the suite can use it — so the evidence lives here rather than in a test.
|
|
18915
19108
|
*
|
|
18916
|
-
* The WINDOWS half of the same contract
|
|
18917
|
-
*
|
|
18918
|
-
* 2026-07-17): `findCredentials` enumerates `account` byte-for-byte as stored —
|
|
19109
|
+
* The WINDOWS half of the same contract is also measured (real Credential Manager,
|
|
19110
|
+
* real binding, throwaway service): `findCredentials` enumerates `account` byte-for-byte as stored —
|
|
18919
19111
|
* case preserved, 48-char refs intact — a confirmed delete empties the
|
|
18920
19112
|
* enumeration, and `deleteCredential` on a nonexistent ref returns the same
|
|
18921
19113
|
* meaningless `false` as Linux. So the `.some(c => c.account === ref)` comparison
|
|
18922
19114
|
* is sound on both measured platforms. Still unmeasured: a CI lane that runs this
|
|
18923
|
-
* against the real Windows store on every
|
|
18924
|
-
* measurement, not a regression gate.
|
|
18925
|
-
* public npm, so no tracker number here.)
|
|
19115
|
+
* against the real Windows store on every change — the dev-machine probe is a point
|
|
19116
|
+
* measurement, not a regression gate.
|
|
18926
19117
|
*
|
|
18927
19118
|
* ⚠️ THIS METHOD'S FAIL-CLOSED GUARANTEE IS AN UPSTREAM BEHAVIOURAL CONTRACT, NOT
|
|
18928
19119
|
* SOMETHING THIS CODE ENFORCES. `@napi-rs/keyring` is pinned to an EXACT version
|
|
@@ -18992,11 +19183,11 @@ var CryptoFileSecretStore = class {
|
|
|
18992
19183
|
/**
|
|
18993
19184
|
* The salt is the one file whose loss is UNRECOVERABLE — every `.enc` is scrypt'd
|
|
18994
19185
|
* against it, so replacing it silently re-keys the store and bricks them all. Two
|
|
18995
|
-
* rules follow
|
|
19186
|
+
* rules follow:
|
|
18996
19187
|
*
|
|
18997
19188
|
* - A present-but-short salt is treated exactly like a corrupt index
|
|
18998
19189
|
* ({@link readKeyring}): throw an actionable error, never silently replace.
|
|
18999
|
-
*
|
|
19190
|
+
* Regenerating it would convert one anomaly into every secret
|
|
19000
19191
|
* being permanently undecryptable — destruction dressed as repair.
|
|
19001
19192
|
* - Creation is temp + hard-link (atomic AND exclusive; below), with an
|
|
19002
19193
|
* exclusive-create (`wx`) fallback on no-hardlink filesystems. The link path
|
|
@@ -19078,7 +19269,7 @@ var CryptoFileSecretStore = class {
|
|
|
19078
19269
|
async setSecret(ref, secret) {
|
|
19079
19270
|
const key = await this.encryptionKey();
|
|
19080
19271
|
const iv = randomBytes2(IV_LEN);
|
|
19081
|
-
const cipher = createCipheriv(ALGO, key, iv);
|
|
19272
|
+
const cipher = createCipheriv(ALGO, key, iv, { authTagLength: TAG_LEN });
|
|
19082
19273
|
const enc = Buffer.concat([cipher.update(secret, "utf8"), cipher.final()]);
|
|
19083
19274
|
const tag = cipher.getAuthTag();
|
|
19084
19275
|
const blob = Buffer.concat([iv, tag, enc]);
|
|
@@ -19101,7 +19292,7 @@ var CryptoFileSecretStore = class {
|
|
|
19101
19292
|
const iv = blob.subarray(0, IV_LEN);
|
|
19102
19293
|
const tag = blob.subarray(IV_LEN, IV_LEN + TAG_LEN);
|
|
19103
19294
|
const enc = blob.subarray(IV_LEN + TAG_LEN);
|
|
19104
|
-
const decipher = createDecipheriv(ALGO, key, iv);
|
|
19295
|
+
const decipher = createDecipheriv(ALGO, key, iv, { authTagLength: TAG_LEN });
|
|
19105
19296
|
decipher.setAuthTag(tag);
|
|
19106
19297
|
return Buffer.concat([decipher.update(enc), decipher.final()]).toString("utf8");
|
|
19107
19298
|
} catch {
|
|
@@ -19147,9 +19338,8 @@ var CryptoFileSecretStore = class {
|
|
|
19147
19338
|
* EACCES, or the directory not being there AT ALL — answers false, i.e. "we know
|
|
19148
19339
|
* nothing".
|
|
19149
19340
|
*
|
|
19150
|
-
* A MISSING `secrets/` reads as "we know nothing", not as an honest empty
|
|
19151
|
-
*
|
|
19152
|
-
* looked right: nothing has ever been stored, so "no secret here" is true. Why it
|
|
19341
|
+
* A MISSING `secrets/` reads as "we know nothing", not as an honest empty. The
|
|
19342
|
+
* tempting reasoning — nothing has ever been stored, so "no secret here" is true —
|
|
19153
19343
|
* is wrong: this is only ever consulted about an entry that is IN the index and
|
|
19154
19344
|
* stamped `crypto-file` — meaning `setSecret` ran, and `setSecret` mkdir's
|
|
19155
19345
|
* `secrets/` before every write. So for the only entries that reach here, "the
|
|
@@ -19160,7 +19350,7 @@ var CryptoFileSecretStore = class {
|
|
|
19160
19350
|
* it went. Calling that `absent` invites `prune` to delete the entry and the only
|
|
19161
19351
|
* record of a keyId whose key is still live.
|
|
19162
19352
|
*
|
|
19163
|
-
* The fresh-install case
|
|
19353
|
+
* The fresh-install case needs no special handling: an install with nothing
|
|
19164
19354
|
* stored has no index entries either, so nothing asks.
|
|
19165
19355
|
*/
|
|
19166
19356
|
async secretsDirIntact() {
|
|
@@ -19277,10 +19467,9 @@ async function withIndexLock(dir, fn, timings = LOCK_OPTS) {
|
|
|
19277
19467
|
// Swallowing is the better answer. We cannot un-write what we already wrote;
|
|
19278
19468
|
// finishing is strictly better than crashing half-done.
|
|
19279
19469
|
//
|
|
19280
|
-
// What swallowing does NOT buy
|
|
19281
|
-
//
|
|
19282
|
-
//
|
|
19283
|
-
// delete a successor's lock. That detection runs on the mtime-refresh timer,
|
|
19470
|
+
// What swallowing does NOT buy is protection from our own release: the library
|
|
19471
|
+
// marks a lock released when it DETECTS a compromise, after which our release
|
|
19472
|
+
// below would be a no-op that cannot delete a successor's lock. That detection runs on the mtime-refresh timer,
|
|
19284
19473
|
// every `stale/2` — 2.5s at the shipping 5s — and this file's critical
|
|
19285
19474
|
// sections are ~1ms, so in practice the holder is gone before the first tick
|
|
19286
19475
|
// and nothing is ever detected. (The probe that observed the no-op ran at
|
|
@@ -19293,15 +19482,13 @@ async function withIndexLock(dir, fn, timings = LOCK_OPTS) {
|
|
|
19293
19482
|
// updateKeyring's identity re-check before each commit — which, per its own
|
|
19294
19483
|
// honesty note, narrows the lost-update window; it does not close it.
|
|
19295
19484
|
//
|
|
19296
|
-
// RESIDUAL, stated honestly
|
|
19297
|
-
//
|
|
19298
|
-
// compromised lock "requires our event loop to freeze past `stale`". It does
|
|
19299
|
-
// not. proper-lockfile's stale-break is `stat` -> judge stale -> `rmdir` ->
|
|
19485
|
+
// RESIDUAL, stated honestly: reaching a compromised lock does NOT require our
|
|
19486
|
+
// event loop to freeze past `stale`. proper-lockfile's stale-break is `stat` -> judge stale -> `rmdir` ->
|
|
19300
19487
|
// `mkdir`, and it never re-checks that the directory it removes is the one it
|
|
19301
19488
|
// judged: two waiters can both condemn one crash remnant, and the second's
|
|
19302
19489
|
// already-decided rmdir destroys the first's FRESH lock. Nothing freezes.
|
|
19303
19490
|
//
|
|
19304
|
-
// MEASURED against this exact config (
|
|
19491
|
+
// MEASURED against this exact config (probe results below):
|
|
19305
19492
|
// real OS processes, no injection : 0/25 and 1/25 (two independent runs)
|
|
19306
19493
|
// in-process, 5 waiters : ~3% per round
|
|
19307
19494
|
// rival's rmdir lagged one turn : 19/25
|
|
@@ -19313,10 +19500,10 @@ async function withIndexLock(dir, fn, timings = LOCK_OPTS) {
|
|
|
19313
19500
|
// round-trip. Rare, not impossible — and inherent to mtime-based locking of
|
|
19314
19501
|
// this shape.
|
|
19315
19502
|
//
|
|
19316
|
-
// Accepted KNOWINGLY.
|
|
19317
|
-
// behind a second mutex — the right idea —
|
|
19318
|
-
// breaker at 10s with no liveness check
|
|
19319
|
-
// injection at all, plus 0.57% of contended acquires dying on Windows EPERM.
|
|
19503
|
+
// Accepted KNOWINGLY. A hand-rolled lock that serialised the break
|
|
19504
|
+
// behind a second mutex — the right idea — hits a defect there: that mutex age-breaks a LIVE
|
|
19505
|
+
// breaker at 10s with no liveness check (`maxOccupancy = 2`, measured with no
|
|
19506
|
+
// injection at all), plus 0.57% of contended acquires dying on Windows EPERM.
|
|
19320
19507
|
// Trading a measured, reachable defect for one that needs injected lag to
|
|
19321
19508
|
// observe is the right trade; pretending it is zero is not. If it ever needs
|
|
19322
19509
|
// closing the fix is upstream (re-stat under the removal), NOT another
|
|
@@ -19446,8 +19633,8 @@ var Keyring = class _Keyring {
|
|
|
19446
19633
|
/**
|
|
19447
19634
|
* Where new secrets go, or undefined if the selected store isn't actually here.
|
|
19448
19635
|
*
|
|
19449
|
-
* That sounds impossible — the file store is always constructed —
|
|
19450
|
-
*
|
|
19636
|
+
* That sounds impossible — the file store is always constructed — so it is tempting
|
|
19637
|
+
* to cast the `undefined` away. That would be wrong:
|
|
19451
19638
|
* `new Keyring({ selection: { kind: 'os-keychain' } })` with no `keychainStore`
|
|
19452
19639
|
* names a store that was never supplied, and a cast does not conjure one. Return
|
|
19453
19640
|
* the honest type and let {@link storeSecretSomewhere} handle it, rather than
|
|
@@ -19625,7 +19812,7 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19625
19812
|
* {@link storeSecretSomewhere} throws — i.e. only when no store on this machine
|
|
19626
19813
|
* will accept a write at all, which is the failure worth stopping for.
|
|
19627
19814
|
*
|
|
19628
|
-
* Still NOT a proof that {@link add} will succeed, and the gap is
|
|
19815
|
+
* Still NOT a proof that {@link add} will succeed, and the remaining gap is only the
|
|
19629
19816
|
* genuinely transient: a lock another process holds for longer than the retry
|
|
19630
19817
|
* budget, or a CAS race, either of which a retry actually fixes. What it DOES
|
|
19631
19818
|
* cover is every deterministic refusal — no writable secret store, and an index
|
|
@@ -19659,11 +19846,11 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19659
19846
|
* delete, and no other store PROVED it still holds a copy.
|
|
19660
19847
|
*
|
|
19661
19848
|
* That is deliberately WEAKER than "proved gone everywhere", and saying the
|
|
19662
|
-
* stronger thing (
|
|
19849
|
+
* stronger thing ("PROVED gone") would hide a real residual:
|
|
19663
19850
|
* a non-home store vetoes only on `present`, and a LOCKED keychain answers
|
|
19664
19851
|
* `unknown` — so on the one state this store's model is built around, a stale copy behind a
|
|
19665
19852
|
* locked non-home store passes unvetoed. That is the chosen trade, not an
|
|
19666
|
-
* oversight: the alternative (veto on doubt)
|
|
19853
|
+
* oversight: the alternative (veto on doubt) would let an unrelated
|
|
19667
19854
|
* locked keychain block removal of file-store entries forever, with advice to
|
|
19668
19855
|
* unlock a store that never held the secret.
|
|
19669
19856
|
*
|
|
@@ -19724,7 +19911,7 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19724
19911
|
* freshly minted `keyId` on every path (`issueOrRotateKey` either creates a new key
|
|
19725
19912
|
* or revokes-and-recreates, and both mint a new id), so `prior.secretRef !==
|
|
19726
19913
|
* stamped.secretRef` always holds and `add` always takes the `forgetEverywhere`
|
|
19727
|
-
* branch.
|
|
19914
|
+
* branch. That does not make this dead code — it is UNEXERCISED by `join`, and
|
|
19728
19915
|
* the distinction matters:
|
|
19729
19916
|
*
|
|
19730
19917
|
* - `add` is public and takes the entry as an argument, so "the caller supplies a
|
|
@@ -19791,10 +19978,10 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19791
19978
|
* `keyId`, so dropping it while the `ssk_*` survives leaves a live, unrevokable
|
|
19792
19979
|
* credential on the machine.
|
|
19793
19980
|
*
|
|
19794
|
-
* The gate is the DELETE'S RESULT, not whether the store looked reachable.
|
|
19795
|
-
*
|
|
19981
|
+
* The gate is the DELETE'S RESULT, not whether the store looked reachable. A
|
|
19982
|
+
* reachability gate would not work, because a
|
|
19796
19983
|
* LOCKED keychain is perfectly reachable — it constructs, it answers, it just
|
|
19797
|
-
* won't do anything. The guard never
|
|
19984
|
+
* won't do anything. The guard would never fire, the delete would quietly fail, the row would be
|
|
19798
19985
|
* dropped. Reachability is a proxy; "is it gone?" is the question.
|
|
19799
19986
|
*
|
|
19800
19987
|
* The secret is destroyed BEFORE the index row, the opposite of {@link add}, and
|
|
@@ -19809,10 +19996,8 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19809
19996
|
* for as long as a user takes to type would time out every other vectros process,
|
|
19810
19997
|
* including an MCP server trying to start. The lock buys nothing here anyway —
|
|
19811
19998
|
* `add` writes its secret outside the lock too, so the two secret-writes were
|
|
19812
|
-
* never serialised by it. So: the INITIAL read is unlocked (
|
|
19813
|
-
*
|
|
19814
|
-
* under a lock" — false too: the commit-time re-check below IS a read inside
|
|
19815
|
-
* `updateKeyring`'s lock). Delete outside the lock, then commit under it,
|
|
19999
|
+
* never serialised by it. So: the INITIAL read is unlocked (the commit-time
|
|
20000
|
+
* re-check below, by contrast, IS a read inside `updateKeyring`'s lock). Delete outside the lock, then commit under it,
|
|
19816
20001
|
* re-checking that the entry is still the one the unlocked read saw. That locked
|
|
19817
20002
|
* re-check is what stands in for having read under the lock to begin with.
|
|
19818
20003
|
*/
|
|
@@ -19962,18 +20147,17 @@ The key itself WAS created server-side and is live \u2014 its id is ${entry.keyI
|
|
|
19962
20147
|
* already HAS the secret, so every failure here is silent and leaves the machine
|
|
19963
20148
|
* as it found it, for the next read to retry.
|
|
19964
20149
|
*
|
|
19965
|
-
* "As it found it" is a promise about BOTH stores
|
|
19966
|
-
*
|
|
19967
|
-
*
|
|
20150
|
+
* "As it found it" is a promise about BOTH stores. Saying failure "simply leaves the
|
|
20151
|
+
* secret where it was" would be false for any abort path that returned after
|
|
20152
|
+
* `setSecret` had already landed a SECOND copy in
|
|
19968
20153
|
* the target and left it there — a copy of a live `ssk_*` that no index row names,
|
|
19969
20154
|
* i.e. the exact strand `forgetEverywhere` exists to prevent, produced by the
|
|
19970
20155
|
* function that migrates.
|
|
19971
20156
|
*
|
|
19972
20157
|
* So the target copy is treated as UNCOMMITTED until the migration actually lands,
|
|
19973
20158
|
* and `finally` clears it on every other route out. That is structural rather than
|
|
19974
|
-
* remembered:
|
|
19975
|
-
*
|
|
19976
|
-
* file a review round. The flag cannot be forgotten on a path added later.
|
|
20159
|
+
* remembered: a hand-written cleanup per abort path is precisely the shape that gets
|
|
20160
|
+
* forgotten on one of them. The flag cannot be forgotten on a path added later.
|
|
19977
20161
|
*
|
|
19978
20162
|
* The ordering is the rest of the design, and each step guards a different disaster:
|
|
19979
20163
|
* write the new copy; read it back (a write that silently didn't land would
|
|
@@ -21134,7 +21318,7 @@ async function resolveBridgeCognitoToken(args, prompt, isTty, resolveCognito = r
|
|
|
21134
21318
|
}
|
|
21135
21319
|
if (!isTty) {
|
|
21136
21320
|
throw new Error(
|
|
21137
|
-
|
|
21321
|
+
`No bridge token: set VECTROS_BOOTSTRAP_TOKEN, pass --token, or run \`${loginCommandFor(args.env)}\` first (required in non-interactive mode).`
|
|
21138
21322
|
);
|
|
21139
21323
|
}
|
|
21140
21324
|
process.stdout.write(
|
|
@@ -21243,7 +21427,9 @@ ${renderPlan(blueprint, planSteps)}
|
|
|
21243
21427
|
`);
|
|
21244
21428
|
const declaredIdentities = Object.entries(blueprint.identities ?? {});
|
|
21245
21429
|
if (declaredIdentities.length > 0) {
|
|
21246
|
-
process.stdout.write(
|
|
21430
|
+
process.stdout.write(`
|
|
21431
|
+
${IDENTITIES_TO_ENSURE_HEADING}
|
|
21432
|
+
`);
|
|
21247
21433
|
for (const [name, decl] of declaredIdentities) {
|
|
21248
21434
|
process.stdout.write(` ~ ${name} (${decl.kind}) \u2014 external id '${decl.externalId}'
|
|
21249
21435
|
`);
|
|
@@ -21474,6 +21660,12 @@ ${codeAddHint}`);
|
|
|
21474
21660
|
}
|
|
21475
21661
|
return finishApply();
|
|
21476
21662
|
} catch (err) {
|
|
21663
|
+
if (err instanceof SessionExpiredError) {
|
|
21664
|
+
process.stderr.write(`
|
|
21665
|
+
\u2716 ${err.message}
|
|
21666
|
+
`);
|
|
21667
|
+
return 2;
|
|
21668
|
+
}
|
|
21477
21669
|
if (err instanceof LostKeyError) {
|
|
21478
21670
|
process.stderr.write(`
|
|
21479
21671
|
\u2716 ${err.message}
|
|
@@ -21845,7 +22037,9 @@ ${renderPlan(blueprint, buildPlan(blueprint, { noSeed, noKey: true }), { prune:
|
|
|
21845
22037
|
process.stdout.write(applySeedNotice(blueprint, args.seed));
|
|
21846
22038
|
const declaredIdentities = Object.entries(blueprint.identities ?? {});
|
|
21847
22039
|
if (declaredIdentities.length > 0) {
|
|
21848
|
-
process.stdout.write(
|
|
22040
|
+
process.stdout.write(`
|
|
22041
|
+
${IDENTITIES_TO_ENSURE_HEADING}
|
|
22042
|
+
`);
|
|
21849
22043
|
for (const [name, decl] of declaredIdentities) {
|
|
21850
22044
|
process.stdout.write(` ~ ${name} (${decl.kind}) \u2014 external id '${decl.externalId}'
|
|
21851
22045
|
`);
|
|
@@ -21978,7 +22172,7 @@ function reportApplyError(err) {
|
|
|
21978
22172
|
If this is expected (a re-apply of a blueprint you manage), re-run with --confirm-existing-principal
|
|
21979
22173
|
(with --yes in a pipeline, where it is standing consent), or confirm at the prompt.
|
|
21980
22174
|
If you don't recognize this identity, investigate the blueprint's servicePrincipal.externalId first.
|
|
21981
|
-
Only the
|
|
22175
|
+
Only the principal lookup had run; no context, grant or anything else was created or changed.
|
|
21982
22176
|
`
|
|
21983
22177
|
);
|
|
21984
22178
|
return 3;
|
|
@@ -22331,10 +22525,14 @@ schemas:
|
|
|
22331
22525
|
accessProfile:
|
|
22332
22526
|
# SCOPE GATE: data-plane only (records/schemas/search/documents/folders/inference/entities),
|
|
22333
22527
|
# PLUS profiles:r/u/d (bounded by the platform to redistributing access you already have,
|
|
22334
|
-
# never granting more)
|
|
22335
|
-
# platform resolves "self" from the caller, not from anything you declare here)
|
|
22336
|
-
#
|
|
22337
|
-
#
|
|
22528
|
+
# never granting more), profiles:c:self (create a profile for yourself only \u2014 the
|
|
22529
|
+
# platform resolves "self" from the caller, not from anything you declare here) and
|
|
22530
|
+
# scripts:x:<script-name> (run one named script that this blueprint ships; there is no bare
|
|
22531
|
+
# or wildcard form).
|
|
22532
|
+
# A bare profiles:c is accepted ONLY when this same clause also declares the
|
|
22533
|
+
# member-lifecycle capability (see "capabilities" below); without it, and always for
|
|
22534
|
+
# profiles:c:usr_<id> naming another principal, it is REJECTED \u2014 no override. Every other
|
|
22535
|
+
# control-plane resource (keys/users/admin/namespaces/triggers/...) and "*" is REJECTED the same way.
|
|
22338
22536
|
# Ops: c|r|u|d, combinable (records:cru). Prefer the narrowest set (least privilege).
|
|
22339
22537
|
allowedActions: [records:r, records:c, records:u, search:r, schemas:r]
|
|
22340
22538
|
# dataScope binds the key to specific owners. Include null to ALSO see
|
|
@@ -22350,8 +22548,10 @@ accessProfile:
|
|
|
22350
22548
|
# above) unless every owner genuinely sharing every record is what you want.
|
|
22351
22549
|
dataScope: {}
|
|
22352
22550
|
# capabilities is a SEPARATE gated dimension from allowedActions \u2014 named platform
|
|
22353
|
-
# effects a resource:ops verb can't express. Only 'member-lifecycle'
|
|
22354
|
-
# today; every other name is
|
|
22551
|
+
# effects a resource:ops verb can't express. Only 'member-lifecycle' and
|
|
22552
|
+
# 'delegate-principal-stamp' are admitted today; every other name is REJECTED, same
|
|
22553
|
+
# posture as the scope gate above. 'member-lifecycle' is also what lets this clause
|
|
22554
|
+
# carry a bare profiles:c.
|
|
22355
22555
|
# capabilities: [member-lifecycle]
|
|
22356
22556
|
|
|
22357
22557
|
servicePrincipal:
|
|
@@ -22438,8 +22638,10 @@ Usage:
|
|
|
22438
22638
|
|
|
22439
22639
|
<file|name> is a path to your own blueprint (YAML .yaml/.yml or JSON .json) OR
|
|
22440
22640
|
the name of a bundled blueprint. The same scope gate that protects bundled
|
|
22441
|
-
blueprints applies to file blueprints
|
|
22442
|
-
|
|
22641
|
+
blueprints applies to file blueprints: beyond data-plane access, a forked or
|
|
22642
|
+
hand-written blueprint can request only a short allowlist of narrow exceptions
|
|
22643
|
+
(profiles:r/u/d, profiles:c:self, a bare profiles:c beside member-lifecycle,
|
|
22644
|
+
scripts:x:<script-name>); every other control-plane scope is rejected.
|
|
22443
22645
|
|
|
22444
22646
|
A blueprint may declare a top-level 'inputs:' block and reference its values
|
|
22445
22647
|
with \${{ inputs.<name> }} (and the built-ins \${{ vectros.context }} /
|
|
@@ -22551,7 +22753,9 @@ ${renderPlan(blueprint, steps)}
|
|
|
22551
22753
|
`);
|
|
22552
22754
|
const declaredIdentities = Object.entries(blueprint.identities ?? {});
|
|
22553
22755
|
if (declaredIdentities.length > 0) {
|
|
22554
|
-
process.stdout.write(
|
|
22756
|
+
process.stdout.write(`
|
|
22757
|
+
${IDENTITIES_TO_ENSURE_HEADING}
|
|
22758
|
+
`);
|
|
22555
22759
|
for (const [name, decl] of declaredIdentities) {
|
|
22556
22760
|
process.stdout.write(` ~ ${name} (${decl.kind}) \u2014 external id '${decl.externalId}'
|
|
22557
22761
|
`);
|
|
@@ -22685,6 +22889,89 @@ ${BLUEPRINT_HELP}
|
|
|
22685
22889
|
// src/program.ts
|
|
22686
22890
|
import { Command, Option } from "commander";
|
|
22687
22891
|
|
|
22892
|
+
// src/dev-api.ts
|
|
22893
|
+
var DeveloperApiError = class extends Error {
|
|
22894
|
+
statusCode;
|
|
22895
|
+
/** The platform's error body, when it answered with one (its message, request id and error code). */
|
|
22896
|
+
body;
|
|
22897
|
+
constructor(message, statusCode, body) {
|
|
22898
|
+
super(message);
|
|
22899
|
+
this.name = "DeveloperApiError";
|
|
22900
|
+
this.statusCode = statusCode;
|
|
22901
|
+
this.body = body;
|
|
22902
|
+
}
|
|
22903
|
+
};
|
|
22904
|
+
async function fetchDeveloperUsersPage(opts) {
|
|
22905
|
+
const { cognitoToken, apiBaseUrl, tenant, startFrom, limit, fetchImpl = fetch } = opts;
|
|
22906
|
+
const base = apiBaseUrl.replace(/\/$/, "");
|
|
22907
|
+
let url = `${base}/developer/users?tenant=${encodeURIComponent(tenant)}`;
|
|
22908
|
+
if (startFrom) url += `&startFrom=${encodeURIComponent(startFrom)}`;
|
|
22909
|
+
if (limit !== void 0) url += `&limit=${encodeURIComponent(String(limit))}`;
|
|
22910
|
+
let res;
|
|
22911
|
+
try {
|
|
22912
|
+
res = await fetchImpl(url, {
|
|
22913
|
+
method: "GET",
|
|
22914
|
+
headers: { Authorization: `Bearer ${cognitoToken}` }
|
|
22915
|
+
});
|
|
22916
|
+
} catch (err) {
|
|
22917
|
+
throw new DeveloperApiError(
|
|
22918
|
+
`Request to /developer/users failed: ${err instanceof Error ? err.message : String(err)}`
|
|
22919
|
+
);
|
|
22920
|
+
}
|
|
22921
|
+
if (!res.ok) {
|
|
22922
|
+
const body = await res.text().catch(() => "");
|
|
22923
|
+
const hint = res.status === 403 ? " (this command requires an account owner login \u2014 sign in as the owner and retry)" : "";
|
|
22924
|
+
throw new DeveloperApiError(
|
|
22925
|
+
`GET /developer/users failed: HTTP ${res.status}${hint}${body ? ` \u2014 ${body}` : ""}`,
|
|
22926
|
+
res.status
|
|
22927
|
+
);
|
|
22928
|
+
}
|
|
22929
|
+
let parsed;
|
|
22930
|
+
try {
|
|
22931
|
+
parsed = await res.json();
|
|
22932
|
+
} catch {
|
|
22933
|
+
throw new DeveloperApiError("GET /developer/users returned a non-JSON body");
|
|
22934
|
+
}
|
|
22935
|
+
const data = Array.isArray(parsed.data) ? parsed.data : [];
|
|
22936
|
+
const nextCursor = typeof parsed.nextCursor === "string" ? parsed.nextCursor : null;
|
|
22937
|
+
return { data, nextCursor };
|
|
22938
|
+
}
|
|
22939
|
+
async function fetchDeveloperMe(opts) {
|
|
22940
|
+
const { cognitoToken, apiBaseUrl, tenant, fetchImpl = fetch } = opts;
|
|
22941
|
+
const url = `${apiBaseUrl.replace(/\/$/, "")}/developer/me?tenant=${encodeURIComponent(tenant)}`;
|
|
22942
|
+
let res;
|
|
22943
|
+
try {
|
|
22944
|
+
res = await fetchImpl(url, { method: "GET", headers: { Authorization: `Bearer ${cognitoToken}` } });
|
|
22945
|
+
} catch (err) {
|
|
22946
|
+
throw new DeveloperApiError(
|
|
22947
|
+
`Request to /developer/me failed: ${err instanceof Error ? err.message : String(err)}`
|
|
22948
|
+
);
|
|
22949
|
+
}
|
|
22950
|
+
if (res.status === 404) return null;
|
|
22951
|
+
if (!res.ok) {
|
|
22952
|
+
const failure = await devRouteError(res, "GET /developer/me");
|
|
22953
|
+
throw new DeveloperApiError(
|
|
22954
|
+
`GET /developer/me failed: HTTP ${res.status}`,
|
|
22955
|
+
res.status,
|
|
22956
|
+
failure.body
|
|
22957
|
+
);
|
|
22958
|
+
}
|
|
22959
|
+
let parsed;
|
|
22960
|
+
try {
|
|
22961
|
+
parsed = await res.json();
|
|
22962
|
+
} catch {
|
|
22963
|
+
throw new DeveloperApiError("GET /developer/me returned a non-JSON body");
|
|
22964
|
+
}
|
|
22965
|
+
if (typeof parsed.principalId !== "string" || typeof parsed.userId !== "string") {
|
|
22966
|
+
throw new DeveloperApiError("GET /developer/me returned no principal");
|
|
22967
|
+
}
|
|
22968
|
+
return {
|
|
22969
|
+
principalId: parsed.principalId,
|
|
22970
|
+
userId: parsed.userId,
|
|
22971
|
+
tenantId: typeof parsed.tenantId === "string" ? parsed.tenantId : void 0
|
|
22972
|
+
};
|
|
22973
|
+
}
|
|
22974
|
+
|
|
22688
22975
|
// src/self-principal.ts
|
|
22689
22976
|
var PRINCIPAL_PREFIX_USER = "usr_";
|
|
22690
22977
|
function emailFromJwt(token) {
|
|
@@ -22713,6 +23000,37 @@ async function resolveSelfPrincipal(client, cognitoToken) {
|
|
|
22713
23000
|
if (!match?.id) return null;
|
|
22714
23001
|
return { principalId: `${PRINCIPAL_PREFIX_USER}${match.id}`, userId: match.id, email };
|
|
22715
23002
|
}
|
|
23003
|
+
async function resolveSelfPrincipalForTenant(auth, fetchImpl) {
|
|
23004
|
+
const tenant = auth.tenant ?? "live";
|
|
23005
|
+
let me;
|
|
23006
|
+
try {
|
|
23007
|
+
me = await fetchDeveloperMe({
|
|
23008
|
+
cognitoToken: auth.cognitoToken,
|
|
23009
|
+
apiBaseUrl: auth.baseUrl,
|
|
23010
|
+
tenant,
|
|
23011
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
23012
|
+
});
|
|
23013
|
+
} catch (err) {
|
|
23014
|
+
if (err instanceof DeveloperApiError && err.statusCode !== void 0) {
|
|
23015
|
+
if (err.body?.errorCode === "SESSION_REFRESH_REQUIRED") throw new SessionExpiredError(auth.env ?? "production");
|
|
23016
|
+
throw err;
|
|
23017
|
+
}
|
|
23018
|
+
if (tenant === "test") throw err;
|
|
23019
|
+
return resolveSelfPrincipal(auth.client, auth.cognitoToken);
|
|
23020
|
+
}
|
|
23021
|
+
if (me === null) return null;
|
|
23022
|
+
const forThisTenant = Boolean(auth.tenantId && me.tenantId && me.tenantId === auth.tenantId);
|
|
23023
|
+
if (forThisTenant && me.principalId.startsWith(PRINCIPAL_PREFIX_USER)) {
|
|
23024
|
+
return { principalId: me.principalId, userId: me.userId, email: emailFromJwt(auth.cognitoToken) ?? "" };
|
|
23025
|
+
}
|
|
23026
|
+
return tenant === "live" ? resolveSelfPrincipal(auth.client, auth.cognitoToken) : null;
|
|
23027
|
+
}
|
|
23028
|
+
function selfPrincipalUnresolvedMessage(tenant, flag) {
|
|
23029
|
+
if (tenant === "test") {
|
|
23030
|
+
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.`;
|
|
23031
|
+
}
|
|
23032
|
+
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\`).`;
|
|
23033
|
+
}
|
|
22716
23034
|
|
|
22717
23035
|
// src/commands/whoami.ts
|
|
22718
23036
|
async function runWhoami(options, deps = {}) {
|
|
@@ -22720,12 +23038,19 @@ async function runWhoami(options, deps = {}) {
|
|
|
22720
23038
|
const { identity, baseUrl } = auth;
|
|
22721
23039
|
let self2 = null;
|
|
22722
23040
|
try {
|
|
22723
|
-
self2 = await
|
|
23041
|
+
self2 = await resolveSelfPrincipalForTenant(
|
|
23042
|
+
{ ...auth, client: auth.client },
|
|
23043
|
+
deps.fetchImpl
|
|
23044
|
+
);
|
|
22724
23045
|
} catch {
|
|
22725
23046
|
self2 = null;
|
|
22726
23047
|
}
|
|
22727
23048
|
if (options.json) {
|
|
22728
|
-
const out =
|
|
23049
|
+
const out = {
|
|
23050
|
+
...identity,
|
|
23051
|
+
tenantType: auth.tenant,
|
|
23052
|
+
...self2 ? { userId: self2.userId, userPrincipalId: self2.principalId } : {}
|
|
23053
|
+
};
|
|
22729
23054
|
process.stdout.write(`${formatJson(out)}
|
|
22730
23055
|
`);
|
|
22731
23056
|
return 0;
|
|
@@ -22734,6 +23059,7 @@ async function runWhoami(options, deps = {}) {
|
|
|
22734
23059
|
process.stdout.write(
|
|
22735
23060
|
"\n" + formatKeyValues([
|
|
22736
23061
|
["tenant", identity.tenantId],
|
|
23062
|
+
["tenant type", auth.tenant],
|
|
22737
23063
|
["environment", identity.environment],
|
|
22738
23064
|
["principal", identity.principalType],
|
|
22739
23065
|
["principal id", identity.principalKeyId],
|
|
@@ -23126,51 +23452,6 @@ ${list}
|
|
|
23126
23452
|
});
|
|
23127
23453
|
}
|
|
23128
23454
|
|
|
23129
|
-
// src/dev-api.ts
|
|
23130
|
-
var DeveloperApiError = class extends Error {
|
|
23131
|
-
statusCode;
|
|
23132
|
-
constructor(message, statusCode) {
|
|
23133
|
-
super(message);
|
|
23134
|
-
this.name = "DeveloperApiError";
|
|
23135
|
-
this.statusCode = statusCode;
|
|
23136
|
-
}
|
|
23137
|
-
};
|
|
23138
|
-
async function fetchDeveloperUsersPage(opts) {
|
|
23139
|
-
const { cognitoToken, apiBaseUrl, tenant, startFrom, limit, fetchImpl = fetch } = opts;
|
|
23140
|
-
const base = apiBaseUrl.replace(/\/$/, "");
|
|
23141
|
-
let url = `${base}/developer/users?tenant=${encodeURIComponent(tenant)}`;
|
|
23142
|
-
if (startFrom) url += `&startFrom=${encodeURIComponent(startFrom)}`;
|
|
23143
|
-
if (limit !== void 0) url += `&limit=${encodeURIComponent(String(limit))}`;
|
|
23144
|
-
let res;
|
|
23145
|
-
try {
|
|
23146
|
-
res = await fetchImpl(url, {
|
|
23147
|
-
method: "GET",
|
|
23148
|
-
headers: { Authorization: `Bearer ${cognitoToken}` }
|
|
23149
|
-
});
|
|
23150
|
-
} catch (err) {
|
|
23151
|
-
throw new DeveloperApiError(
|
|
23152
|
-
`Request to /developer/users failed: ${err instanceof Error ? err.message : String(err)}`
|
|
23153
|
-
);
|
|
23154
|
-
}
|
|
23155
|
-
if (!res.ok) {
|
|
23156
|
-
const body = await res.text().catch(() => "");
|
|
23157
|
-
const hint = res.status === 403 ? " (this command requires an account owner login \u2014 sign in as the owner and retry)" : "";
|
|
23158
|
-
throw new DeveloperApiError(
|
|
23159
|
-
`GET /developer/users failed: HTTP ${res.status}${hint}${body ? ` \u2014 ${body}` : ""}`,
|
|
23160
|
-
res.status
|
|
23161
|
-
);
|
|
23162
|
-
}
|
|
23163
|
-
let parsed;
|
|
23164
|
-
try {
|
|
23165
|
-
parsed = await res.json();
|
|
23166
|
-
} catch {
|
|
23167
|
-
throw new DeveloperApiError("GET /developer/users returned a non-JSON body");
|
|
23168
|
-
}
|
|
23169
|
-
const data = Array.isArray(parsed.data) ? parsed.data : [];
|
|
23170
|
-
const nextCursor = typeof parsed.nextCursor === "string" ? parsed.nextCursor : null;
|
|
23171
|
-
return { data, nextCursor };
|
|
23172
|
-
}
|
|
23173
|
-
|
|
23174
23455
|
// src/commands/identity.ts
|
|
23175
23456
|
function isUserDimension(type) {
|
|
23176
23457
|
return type === "user";
|
|
@@ -23466,14 +23747,13 @@ function assertPrincipalShape(principal) {
|
|
|
23466
23747
|
}
|
|
23467
23748
|
}
|
|
23468
23749
|
var SELF_PRINCIPAL_ALIAS = "me";
|
|
23469
|
-
async function resolvePrincipalArg(raw, auth) {
|
|
23750
|
+
async function resolvePrincipalArg(raw, auth, deps) {
|
|
23470
23751
|
if (raw !== SELF_PRINCIPAL_ALIAS) return raw;
|
|
23471
|
-
const self2 = await
|
|
23472
|
-
|
|
23473
|
-
|
|
23474
|
-
|
|
23475
|
-
|
|
23476
|
-
}
|
|
23752
|
+
const self2 = await resolveSelfPrincipalForTenant(
|
|
23753
|
+
{ ...auth, client: auth.client },
|
|
23754
|
+
deps.fetchImpl
|
|
23755
|
+
);
|
|
23756
|
+
if (!self2) throw new Error(selfPrincipalUnresolvedMessage(auth.tenant, "--principal"));
|
|
23477
23757
|
return self2.principalId;
|
|
23478
23758
|
}
|
|
23479
23759
|
function roleSummary(p) {
|
|
@@ -23526,7 +23806,7 @@ async function runAccessGrant(opts, deps = {}) {
|
|
|
23526
23806
|
return withAuth(opts, deps, async (auth) => {
|
|
23527
23807
|
const client = auth.client;
|
|
23528
23808
|
try {
|
|
23529
|
-
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
23809
|
+
const principal = await resolvePrincipalArg(opts.principal, auth, deps);
|
|
23530
23810
|
const scopes = hasActions ? [{ allowed_actions: splitCsv(opts.actions) }] : void 0;
|
|
23531
23811
|
const profile = await client.auth.createAccessProfile({
|
|
23532
23812
|
contextId: opts.context,
|
|
@@ -23536,8 +23816,10 @@ async function runAccessGrant(opts, deps = {}) {
|
|
|
23536
23816
|
// Always send the plural `roleIds`, even for a single
|
|
23537
23817
|
// `--role`: it is a strict superset of the deprecated singular
|
|
23538
23818
|
// `roleId` (equivalent to `roleIds: ["<value>"]`), and the platform
|
|
23539
|
-
// reads it uniformly regardless of how many roles compose.
|
|
23540
|
-
|
|
23819
|
+
// reads it uniformly regardless of how many roles compose. With
|
|
23820
|
+
// `--actions` there is no role: omit `roleIds` (the platform refuses an
|
|
23821
|
+
// empty list next to inline `scopes`).
|
|
23822
|
+
roleIds: hasRole ? opts.role : void 0,
|
|
23541
23823
|
scopes,
|
|
23542
23824
|
identityOverrides
|
|
23543
23825
|
}
|
|
@@ -23574,7 +23856,7 @@ async function runAccessRevoke(opts, deps = {}) {
|
|
|
23574
23856
|
return withAuth(opts, deps, async (auth) => {
|
|
23575
23857
|
const client = auth.client;
|
|
23576
23858
|
try {
|
|
23577
|
-
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
23859
|
+
const principal = await resolvePrincipalArg(opts.principal, auth, deps);
|
|
23578
23860
|
await client.auth.deleteAccessProfile({ contextId: opts.context, principalId: principal });
|
|
23579
23861
|
process.stdout.write(` \u2714 Revoked ${principal} from '${opts.context}'
|
|
23580
23862
|
`);
|
|
@@ -23586,6 +23868,12 @@ async function runAccessRevoke(opts, deps = {}) {
|
|
|
23586
23868
|
}
|
|
23587
23869
|
});
|
|
23588
23870
|
}
|
|
23871
|
+
function searchedContextNotice(principal, rows, tenant) {
|
|
23872
|
+
if (rows.some((r) => r.contextId !== void 0 && r.contextId !== DEFAULT_CONTEXT_ID)) return void 0;
|
|
23873
|
+
const tenantFlag = tenant === "test" ? " --tenant test" : "";
|
|
23874
|
+
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}
|
|
23875
|
+
`;
|
|
23876
|
+
}
|
|
23589
23877
|
async function runAccessList(opts, deps = {}) {
|
|
23590
23878
|
if (Boolean(opts.context) === Boolean(opts.principal)) {
|
|
23591
23879
|
process.stderr.write(" \u2716 Provide exactly one of --context <c> or --principal <p>.\n");
|
|
@@ -23603,15 +23891,17 @@ async function runAccessList(opts, deps = {}) {
|
|
|
23603
23891
|
return withAuth(opts, deps, async (auth) => {
|
|
23604
23892
|
const client = auth.client;
|
|
23605
23893
|
try {
|
|
23606
|
-
const principal = opts.principal ? await resolvePrincipalArg(opts.principal, auth) : void 0;
|
|
23894
|
+
const principal = opts.principal ? await resolvePrincipalArg(opts.principal, auth, deps) : void 0;
|
|
23607
23895
|
const rows = opts.context ? await drainPages(
|
|
23608
23896
|
(startFrom) => client.auth.listAccessProfiles({ contextId: opts.context, startFrom, limit: PAGE_LIMIT })
|
|
23609
23897
|
) : await drainPages(
|
|
23610
23898
|
(startFrom) => client.auth.listProfilesForPrincipal({ principalId: principal, startFrom, limit: PAGE_LIMIT })
|
|
23611
23899
|
);
|
|
23900
|
+
const notice = opts.principal ? searchedContextNotice(opts.principal, rows, auth.tenant) : void 0;
|
|
23612
23901
|
if (opts.json) {
|
|
23613
23902
|
process.stdout.write(`${formatJson(rows)}
|
|
23614
23903
|
`);
|
|
23904
|
+
if (notice) process.stderr.write(notice);
|
|
23615
23905
|
return 0;
|
|
23616
23906
|
}
|
|
23617
23907
|
process.stdout.write(
|
|
@@ -23622,6 +23912,7 @@ async function runAccessList(opts, deps = {}) {
|
|
|
23622
23912
|
{ header: "status", get: (r) => r.status }
|
|
23623
23913
|
]) + "\n"
|
|
23624
23914
|
);
|
|
23915
|
+
if (notice) process.stderr.write(notice);
|
|
23625
23916
|
return 0;
|
|
23626
23917
|
} catch (err) {
|
|
23627
23918
|
process.stderr.write(` \u2716 access list failed: ${formatError(err)}
|
|
@@ -23643,7 +23934,7 @@ async function runAccessGet(opts, deps = {}) {
|
|
|
23643
23934
|
return withAuth(opts, deps, async (auth) => {
|
|
23644
23935
|
const client = auth.client;
|
|
23645
23936
|
try {
|
|
23646
|
-
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
23937
|
+
const principal = await resolvePrincipalArg(opts.principal, auth, deps);
|
|
23647
23938
|
const p = await client.auth.getAccessProfile({ contextId: opts.context, principalId: principal });
|
|
23648
23939
|
if (opts.json) {
|
|
23649
23940
|
process.stdout.write(`${formatJson(p)}
|
|
@@ -23865,11 +24156,13 @@ async function runAccessExplain(opts, deps = {}) {
|
|
|
23865
24156
|
try {
|
|
23866
24157
|
let principal = opts.principal;
|
|
23867
24158
|
if (principal === SELF_PRINCIPAL_ALIAS2) {
|
|
23868
|
-
const self2 = await
|
|
24159
|
+
const self2 = await resolveSelfPrincipalForTenant(
|
|
24160
|
+
{ ...auth, client: auth.client },
|
|
24161
|
+
deps.fetchImpl
|
|
24162
|
+
);
|
|
23869
24163
|
if (!self2) {
|
|
23870
|
-
process.stderr.write(
|
|
23871
|
-
|
|
23872
|
-
);
|
|
24164
|
+
process.stderr.write(` \u2716 ${selfPrincipalUnresolvedMessage(auth.tenant, "--principal")}
|
|
24165
|
+
`);
|
|
23873
24166
|
return 1;
|
|
23874
24167
|
}
|
|
23875
24168
|
principal = self2.principalId;
|
|
@@ -24248,10 +24541,17 @@ async function runContextDestroy(opts, deps = {}) {
|
|
|
24248
24541
|
`);
|
|
24249
24542
|
return 0;
|
|
24250
24543
|
}
|
|
24544
|
+
const tenantFlag = auth.tenant === "test" ? " --tenant test" : "";
|
|
24251
24545
|
process.stdout.write(
|
|
24252
24546
|
`
|
|
24253
24547
|
\u2714 teardown of '${contextId}' started \u2014 the context and its contents are being
|
|
24254
24548
|
deleted asynchronously (it reports status 'purging' until the drain completes)
|
|
24549
|
+
|
|
24550
|
+
Not removed: a service principal a blueprint bootstrap created for this context (the
|
|
24551
|
+
externalId in the blueprint's servicePrincipal) belongs to the whole tenant, so it stays. To clean it up
|
|
24552
|
+
when it exists and nothing else uses it:
|
|
24553
|
+
vectros identity list-all${tenantFlag}
|
|
24554
|
+
vectros identity delete --type user --id <id>${tenantFlag}
|
|
24255
24555
|
`
|
|
24256
24556
|
);
|
|
24257
24557
|
return 0;
|
|
@@ -24790,7 +25090,7 @@ ${renderIssuer(issuer)}
|
|
|
24790
25090
|
import { createReadStream as createReadStream2 } from "fs";
|
|
24791
25091
|
import * as readline6 from "readline/promises";
|
|
24792
25092
|
var PROVIDER_TYPES = ["ANTHROPIC_NATIVE", "OPENAI_COMPATIBLE"];
|
|
24793
|
-
var BILLING_MODES = ["
|
|
25093
|
+
var BILLING_MODES = ["CUSTOMER_DIRECT"];
|
|
24794
25094
|
var STATUSES = ["ACTIVE", "DISABLED"];
|
|
24795
25095
|
var MAX_SECRET_BYTES = 8 * 1024;
|
|
24796
25096
|
async function readBoundedSecret(stream) {
|
|
@@ -24869,12 +25169,17 @@ function renderProviderConfig(c) {
|
|
|
24869
25169
|
["defaultProviderModelId", c.defaultProviderModelId],
|
|
24870
25170
|
["waiverType", c.waiverType],
|
|
24871
25171
|
["billingMode", c.billingMode],
|
|
24872
|
-
["platformTokenFactor", c.platformTokenFactor],
|
|
24873
|
-
["platformOutputWeight", c.platformOutputWeight],
|
|
24874
25172
|
["status", c.status],
|
|
24875
25173
|
["createdAt", c.createdAt]
|
|
24876
25174
|
]);
|
|
24877
25175
|
}
|
|
25176
|
+
function billingModeError(value) {
|
|
25177
|
+
if (value === void 0 || BILLING_MODES.includes(value)) return void 0;
|
|
25178
|
+
if (value === "PLATFORM_BILLED") {
|
|
25179
|
+
return "--billing-mode PLATFORM_BILLED is retired: a provider you configure bills you directly, so CUSTOMER_DIRECT is the only billing mode.";
|
|
25180
|
+
}
|
|
25181
|
+
return invalidEnum("--billing-mode", value, BILLING_MODES);
|
|
25182
|
+
}
|
|
24878
25183
|
function invalidEnum(flag, value, allowed) {
|
|
24879
25184
|
return `${flag} must be one of ${allowed.join(", ")} (got '${value}').`;
|
|
24880
25185
|
}
|
|
@@ -24885,8 +25190,9 @@ async function runProviderConfigCreate(opts, deps = {}) {
|
|
|
24885
25190
|
`);
|
|
24886
25191
|
return 2;
|
|
24887
25192
|
}
|
|
24888
|
-
|
|
24889
|
-
|
|
25193
|
+
const billingModeProblem = billingModeError(opts.billingMode);
|
|
25194
|
+
if (billingModeProblem !== void 0) {
|
|
25195
|
+
process.stderr.write(` \u2716 ${billingModeProblem}
|
|
24890
25196
|
`);
|
|
24891
25197
|
return 2;
|
|
24892
25198
|
}
|
|
@@ -24895,19 +25201,6 @@ async function runProviderConfigCreate(opts, deps = {}) {
|
|
|
24895
25201
|
`);
|
|
24896
25202
|
return 2;
|
|
24897
25203
|
}
|
|
24898
|
-
if (opts.billingMode === "PLATFORM_BILLED") {
|
|
24899
|
-
if (opts.platformTokenFactor === void 0 || opts.platformOutputWeight === void 0) {
|
|
24900
|
-
process.stderr.write(
|
|
24901
|
-
" \u2716 --platform-token-factor and --platform-output-weight are required when --billing-mode PLATFORM_BILLED.\n"
|
|
24902
|
-
);
|
|
24903
|
-
return 2;
|
|
24904
|
-
}
|
|
24905
|
-
} else if (opts.platformTokenFactor !== void 0 || opts.platformOutputWeight !== void 0) {
|
|
24906
|
-
process.stderr.write(
|
|
24907
|
-
" \u2716 --platform-token-factor/--platform-output-weight only apply with --billing-mode PLATFORM_BILLED.\n"
|
|
24908
|
-
);
|
|
24909
|
-
return 2;
|
|
24910
|
-
}
|
|
24911
25204
|
let apiKeySecret;
|
|
24912
25205
|
try {
|
|
24913
25206
|
apiKeySecret = await resolveApiKeySecret(opts.apiKeySecretFile, deps, "required");
|
|
@@ -24932,9 +25225,7 @@ async function runProviderConfigCreate(opts, deps = {}) {
|
|
|
24932
25225
|
defaultProviderModelId: opts.defaultProviderModelId,
|
|
24933
25226
|
apiKeySecret,
|
|
24934
25227
|
waiverType: opts.waiverType,
|
|
24935
|
-
billingMode: opts.billingMode,
|
|
24936
|
-
platformTokenFactor: opts.platformTokenFactor,
|
|
24937
|
-
platformOutputWeight: opts.platformOutputWeight,
|
|
25228
|
+
billingMode: opts.billingMode ?? "CUSTOMER_DIRECT",
|
|
24938
25229
|
status: opts.status
|
|
24939
25230
|
})
|
|
24940
25231
|
});
|
|
@@ -25015,8 +25306,9 @@ async function runProviderConfigUpdate(opts, deps = {}) {
|
|
|
25015
25306
|
`);
|
|
25016
25307
|
return 2;
|
|
25017
25308
|
}
|
|
25018
|
-
|
|
25019
|
-
|
|
25309
|
+
const billingModeProblem = billingModeError(opts.billingMode);
|
|
25310
|
+
if (billingModeProblem !== void 0) {
|
|
25311
|
+
process.stderr.write(` \u2716 ${billingModeProblem}
|
|
25020
25312
|
`);
|
|
25021
25313
|
return 2;
|
|
25022
25314
|
}
|
|
@@ -25049,8 +25341,6 @@ async function runProviderConfigUpdate(opts, deps = {}) {
|
|
|
25049
25341
|
apiKeySecret,
|
|
25050
25342
|
waiverType: opts.waiverType,
|
|
25051
25343
|
billingMode: opts.billingMode,
|
|
25052
|
-
platformTokenFactor: opts.platformTokenFactor,
|
|
25053
|
-
platformOutputWeight: opts.platformOutputWeight,
|
|
25054
25344
|
status: opts.status
|
|
25055
25345
|
})
|
|
25056
25346
|
});
|
|
@@ -26627,15 +26917,14 @@ async function runBlueprintTest(fileOrName, opts, deps = {}) {
|
|
|
26627
26917
|
|
|
26628
26918
|
// src/commands/join.ts
|
|
26629
26919
|
var SELF_ALIAS = "me";
|
|
26630
|
-
async function resolvePrincipal(as, auth) {
|
|
26920
|
+
async function resolvePrincipal(as, auth, deps) {
|
|
26631
26921
|
const raw = as ?? SELF_ALIAS;
|
|
26632
26922
|
if (raw === SELF_ALIAS) {
|
|
26633
|
-
const self2 = await
|
|
26634
|
-
|
|
26635
|
-
|
|
26636
|
-
|
|
26637
|
-
|
|
26638
|
-
}
|
|
26923
|
+
const self2 = await resolveSelfPrincipalForTenant(
|
|
26924
|
+
{ ...auth, client: auth.client },
|
|
26925
|
+
deps.fetchImpl
|
|
26926
|
+
);
|
|
26927
|
+
if (!self2) throw new Error(selfPrincipalUnresolvedMessage(auth.tenant, "--as"));
|
|
26639
26928
|
return self2.principalId;
|
|
26640
26929
|
}
|
|
26641
26930
|
if (!raw.startsWith("usr_")) {
|
|
@@ -26705,7 +26994,7 @@ async function runJoin(opts, deps = {}) {
|
|
|
26705
26994
|
const client = auth.client;
|
|
26706
26995
|
try {
|
|
26707
26996
|
const tenantId = requireTenantId(auth);
|
|
26708
|
-
const principalId = await resolvePrincipal(opts.as, auth);
|
|
26997
|
+
const principalId = await resolvePrincipal(opts.as, auth, deps);
|
|
26709
26998
|
const userId = normalizePrincipalUserId(principalId);
|
|
26710
26999
|
const scopes = hasActions ? [{ allowed_actions: splitCsv(opts.actions) }] : void 0;
|
|
26711
27000
|
await client.auth.createAccessProfile({
|
|
@@ -26716,7 +27005,9 @@ async function runJoin(opts, deps = {}) {
|
|
|
26716
27005
|
// Always send the plural `roleIds`, even for a single
|
|
26717
27006
|
// `--role`: a strict superset of the deprecated singular `roleId`,
|
|
26718
27007
|
// read uniformly by the platform regardless of how many compose.
|
|
26719
|
-
|
|
27008
|
+
// With `--actions` there is no role: omit `roleIds` (the platform
|
|
27009
|
+
// refuses an empty list next to inline `scopes`).
|
|
27010
|
+
roleIds: hasRole ? opts.role : void 0,
|
|
26720
27011
|
scopes,
|
|
26721
27012
|
identityOverrides
|
|
26722
27013
|
}
|
|
@@ -26803,9 +27094,9 @@ function emitJoined(opts) {
|
|
|
26803
27094
|
|
|
26804
27095
|
// src/build-info.ts
|
|
26805
27096
|
var BUILD_INFO = {
|
|
26806
|
-
cli: true ? "0.
|
|
26807
|
-
sdk: true ? "0.
|
|
26808
|
-
blueprints: true ? "0.20.
|
|
27097
|
+
cli: true ? "0.26.0" : "dev",
|
|
27098
|
+
sdk: true ? "0.48.0" : "dev",
|
|
27099
|
+
blueprints: true ? "0.20.3" : "dev"
|
|
26809
27100
|
};
|
|
26810
27101
|
function formatBuildInfo() {
|
|
26811
27102
|
return `vectros ${BUILD_INFO.cli} (sdk ${BUILD_INFO.sdk}, blueprints ${BUILD_INFO.blueprints})`;
|
|
@@ -26842,18 +27133,6 @@ function parseTenantFlag(raw) {
|
|
|
26842
27133
|
}
|
|
26843
27134
|
return raw;
|
|
26844
27135
|
}
|
|
26845
|
-
var NUMBER_FLAG_INVALID = /* @__PURE__ */ Symbol("parseOptionalNumberFlag:invalid");
|
|
26846
|
-
function parseOptionalNumberFlag(flag, raw) {
|
|
26847
|
-
if (raw === void 0) return void 0;
|
|
26848
|
-
const n = Number(raw);
|
|
26849
|
-
if (!Number.isFinite(n)) {
|
|
26850
|
-
process.stderr.write(` \u2716 ${flag} must be a number (got '${raw}')
|
|
26851
|
-
`);
|
|
26852
|
-
process.exitCode = 2;
|
|
26853
|
-
return NUMBER_FLAG_INVALID;
|
|
26854
|
-
}
|
|
26855
|
-
return n;
|
|
26856
|
-
}
|
|
26857
27136
|
function toCommonOptions(flags) {
|
|
26858
27137
|
return {
|
|
26859
27138
|
env: flags.env,
|
|
@@ -26876,9 +27155,11 @@ function buildProgram(deps = {}) {
|
|
|
26876
27155
|
program.command("logout").description("Clear the stored login session.").action(async () => {
|
|
26877
27156
|
process.exitCode = await runLogout({ env: "production", json: false });
|
|
26878
27157
|
});
|
|
26879
|
-
const whoami = program.command("whoami").description("Show the authenticated session (tenant, env, principal).");
|
|
27158
|
+
const whoami = program.command("whoami").description("Show the authenticated session (tenant, env, principal).").option("--tenant <tenant>", "tenant to show: 'test' or 'live' (default live)");
|
|
26880
27159
|
addCommonAuthOptions(whoami).action(async (flags) => {
|
|
26881
|
-
|
|
27160
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27161
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27162
|
+
process.exitCode = await runWhoami({ ...toCommonOptions(flags), tenant }, deps);
|
|
26882
27163
|
});
|
|
26883
27164
|
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)");
|
|
26884
27165
|
addCommonAuthOptions(blueprintTest).action(
|
|
@@ -27025,7 +27306,7 @@ function registerContextCommands(program, deps) {
|
|
|
27025
27306
|
);
|
|
27026
27307
|
});
|
|
27027
27308
|
const destroy = context.command("destroy").description(
|
|
27028
|
-
"Permanently destroy an app context and ALL of its data (irreversible; asks for confirmation)."
|
|
27309
|
+
"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'."
|
|
27029
27310
|
).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)");
|
|
27030
27311
|
addCommonAuthOptions(destroy).action(
|
|
27031
27312
|
async (contextId, flags) => {
|
|
@@ -27180,15 +27461,11 @@ function registerIssuersCommands(program, deps) {
|
|
|
27180
27461
|
}
|
|
27181
27462
|
function registerProviderConfigCommands(program, deps) {
|
|
27182
27463
|
const providerConfig = program.command("provider-config").description("Manage BYOM (bring-your-own-model) tenant-level provider configs.");
|
|
27183
|
-
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
|
|
27464
|
+
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)");
|
|
27184
27465
|
addCommonAuthOptions(create).action(
|
|
27185
27466
|
async (flags) => {
|
|
27186
27467
|
const tenant = parseTenantFlag(flags.tenant);
|
|
27187
27468
|
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27188
|
-
const platformTokenFactor = parseOptionalNumberFlag("--platform-token-factor", flags.platformTokenFactor);
|
|
27189
|
-
if (platformTokenFactor === NUMBER_FLAG_INVALID) return;
|
|
27190
|
-
const platformOutputWeight = parseOptionalNumberFlag("--platform-output-weight", flags.platformOutputWeight);
|
|
27191
|
-
if (platformOutputWeight === NUMBER_FLAG_INVALID) return;
|
|
27192
27469
|
process.exitCode = await runProviderConfigCreate(
|
|
27193
27470
|
{
|
|
27194
27471
|
...toCommonOptions(flags),
|
|
@@ -27200,8 +27477,6 @@ function registerProviderConfigCommands(program, deps) {
|
|
|
27200
27477
|
apiKeySecretFile: flags.apiKeySecretFile,
|
|
27201
27478
|
waiverType: flags.waiverType,
|
|
27202
27479
|
billingMode: flags.billingMode,
|
|
27203
|
-
platformTokenFactor,
|
|
27204
|
-
platformOutputWeight,
|
|
27205
27480
|
status: flags.status,
|
|
27206
27481
|
tenant
|
|
27207
27482
|
},
|
|
@@ -27219,13 +27494,9 @@ function registerProviderConfigCommands(program, deps) {
|
|
|
27219
27494
|
addCommonAuthOptions(get).action(async (id, flags) => {
|
|
27220
27495
|
process.exitCode = await runProviderConfigGet({ ...toCommonOptions(flags), id }, deps);
|
|
27221
27496
|
});
|
|
27222
|
-
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 (
|
|
27497
|
+
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");
|
|
27223
27498
|
addCommonAuthOptions(update).action(
|
|
27224
27499
|
async (id, flags) => {
|
|
27225
|
-
const platformTokenFactor = parseOptionalNumberFlag("--platform-token-factor", flags.platformTokenFactor);
|
|
27226
|
-
if (platformTokenFactor === NUMBER_FLAG_INVALID) return;
|
|
27227
|
-
const platformOutputWeight = parseOptionalNumberFlag("--platform-output-weight", flags.platformOutputWeight);
|
|
27228
|
-
if (platformOutputWeight === NUMBER_FLAG_INVALID) return;
|
|
27229
27500
|
process.exitCode = await runProviderConfigUpdate(
|
|
27230
27501
|
{
|
|
27231
27502
|
...toCommonOptions(flags),
|
|
@@ -27238,8 +27509,6 @@ function registerProviderConfigCommands(program, deps) {
|
|
|
27238
27509
|
apiKeySecretFile: flags.apiKeySecretFile,
|
|
27239
27510
|
waiverType: flags.waiverType,
|
|
27240
27511
|
billingMode: flags.billingMode,
|
|
27241
|
-
platformTokenFactor,
|
|
27242
|
-
platformOutputWeight,
|
|
27243
27512
|
status: flags.status
|
|
27244
27513
|
},
|
|
27245
27514
|
deps
|
|
@@ -27507,12 +27776,15 @@ function dimensionOption() {
|
|
|
27507
27776
|
).makeOptionMandatory();
|
|
27508
27777
|
}
|
|
27509
27778
|
function registerIdentityCommands(program, deps) {
|
|
27510
|
-
const identity = program.command("identity").description("Manage tenant-wide
|
|
27511
|
-
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");
|
|
27779
|
+
const identity = program.command("identity").description("Manage principals: users (tenant-wide) and namespaced entities (an entity lives with its namespace) \u2014 reference, never embed.");
|
|
27780
|
+
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)");
|
|
27512
27781
|
addCommonAuthOptions(create).action(
|
|
27513
27782
|
async (flags) => {
|
|
27783
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27784
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27514
27785
|
process.exitCode = await runIdentityCreate({
|
|
27515
27786
|
...toCommonOptions(flags),
|
|
27787
|
+
tenant,
|
|
27516
27788
|
type: flags.type,
|
|
27517
27789
|
externalId: flags.externalId,
|
|
27518
27790
|
name: flags.name,
|
|
@@ -27523,11 +27795,14 @@ function registerIdentityCommands(program, deps) {
|
|
|
27523
27795
|
}, deps);
|
|
27524
27796
|
}
|
|
27525
27797
|
);
|
|
27526
|
-
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));
|
|
27798
|
+
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)");
|
|
27527
27799
|
addCommonAuthOptions(list).action(
|
|
27528
27800
|
async (flags) => {
|
|
27801
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27802
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27529
27803
|
process.exitCode = await runIdentityList({
|
|
27530
27804
|
...toCommonOptions(flags),
|
|
27805
|
+
tenant,
|
|
27531
27806
|
type: flags.type,
|
|
27532
27807
|
externalId: flags.externalId,
|
|
27533
27808
|
limit: flags.limit
|
|
@@ -27547,20 +27822,30 @@ function registerIdentityCommands(program, deps) {
|
|
|
27547
27822
|
);
|
|
27548
27823
|
}
|
|
27549
27824
|
);
|
|
27550
|
-
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)");
|
|
27825
|
+
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)");
|
|
27551
27826
|
addCommonAuthOptions(get).action(async (flags) => {
|
|
27552
|
-
|
|
27827
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27828
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27829
|
+
process.exitCode = await runIdentityGet({ ...toCommonOptions(flags), tenant, type: flags.type, id: flags.id }, deps);
|
|
27553
27830
|
});
|
|
27554
|
-
const del = identity.command("delete").description("Delete a principal by its Vectros id.").addOption(dimensionOption()).requiredOption("--id <vectrosId>", "the Vectros-assigned id");
|
|
27831
|
+
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)");
|
|
27555
27832
|
addAuthOptions(del).action(async (flags) => {
|
|
27556
|
-
|
|
27833
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27834
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27835
|
+
process.exitCode = await runIdentityDelete(
|
|
27836
|
+
{ ...toCommonOptions(flags), tenant, type: flags.type, id: flags.id },
|
|
27837
|
+
deps
|
|
27838
|
+
);
|
|
27557
27839
|
});
|
|
27558
|
-
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");
|
|
27840
|
+
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)");
|
|
27559
27841
|
addCommonAuthOptions(invite).action(
|
|
27560
27842
|
async (flags) => {
|
|
27843
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27844
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27561
27845
|
process.exitCode = await runIdentityInvite(
|
|
27562
27846
|
{
|
|
27563
27847
|
...toCommonOptions(flags),
|
|
27848
|
+
tenant,
|
|
27564
27849
|
context: flags.context,
|
|
27565
27850
|
role: flags.role,
|
|
27566
27851
|
email: flags.email,
|