@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/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. This is the disclosure-accounting surface from which a covered entity derives its HIPAA §164.528 accounting of disclosures. 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.
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; suspending the user does not stop their existing keys at all. A `PENDING` user is fine.
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 a partner with the corresponding account-level 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.
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 back to domain-less; 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.
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 your platform operator, who can force-release this registration so a replacement may be registered in the SAME context under a new issuerId. **The operator 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.
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 partner-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 today, 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.
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 a right-to-erasure request for a single end-subject (a user, or an identity entity in any namespace). Erasure removes exactly the data the subject solely owns across the declared contexts, plus its identity and lookup rows when all contexts are covered. It never touches another account's data and never cascades into another subject's data. 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.
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 verifiable 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.
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
- * Submits an asynchronous job to export your account's data, or a single end-subject's data, across the requested contexts. The request returns 202 with an `exportJobId`; poll `GET /v1/admin/export/{id}` until the job completes to obtain a short-lived presigned download URL and a manifest describing the payload. Requires a root API key — a scoped credential is rejected with 403.
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
- * Polls an export job by id. While the job is still running this returns its status only; once it completes, the response also includes a short-lived presigned download URL and a manifest (format version and per-context counts). Requires a root API key.
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: always available 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 /{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.
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 in this release (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.
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 partner-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.
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 every inference model available to you, including each model's context window, the plan tiers it's available on, and the exact credit rates charged per 1K input and output tokens. Use this to populate model pickers and to validate a request before calling `/v1/chat`, `/v1/rag`, or `/v1/documents/{id}/ask`.
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. Token cost is debited from your pre-paid inference balance (in cents), and a small per-call flat fee is debited from your monthly platform credit allowance. Requires the `inference:r` scope.
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. It currently returns 501 (not implemented). The documented 200 response schema is the stable shape this endpoint will use once available. Requires the `records:r` scope.
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 (or 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.
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 in this release — 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.
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 does not until the platform's repair lands — so a `200` guarantees the rows, and a document's text should be read before it is relied on.
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 takes the first by order). Format passthrough →
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
- // (a cross-consistency lint is deferred to the lint slice).
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 gap found in
12457
- // this pass, since it sits at the confirmation prompt's own disclosure text, not merely
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 until this pass), but the same "costs nothing to close now" standard
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, after that wrap broke a real, multi-line-authored
13469
- // blueprint's `description` in review. `description` is never interpolated into any
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` — confirmed by grep, not assumed), so
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) — a gap found after the rest
13487
- * of this file's forgery-character pass had already shipped; same shape as
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` need no entry — they exist already.
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
- // and fixed alongside it originally), missed here in the first pass; same ordering rule
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
- // validated against deployed staging hosted UI (2026-06-05)
16515
+ // deployed staging hosted UI client
16359
16516
  production: "63eipeedjv78q653k45niih46f"
16360
- // prod SSM /vectros/cli/client-id (validated 2026-06-24)
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 (the public DeveloperUserPoolCliClientId from the deploy) \u2014 baking + a discovery endpoint are tracked follow-ups.`
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. Set ${COGNITO_TOKEN_ENV} or pass --token (generate a token in the developer portal, or run \`vectros login\` once it ships).`
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
- const tokens = await refreshTokens(config, session.refreshToken);
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 ?? "default", tenant });
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 — which is what
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 — the loader previously DROPPED this (the
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
- // was renamed `metadata` → `payload`. Same semantics (arbitrary key/value).
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 — previously dropped by the loader. Ownership mirrors
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. (An earlier opening
18835
- * claimed a "permanent property… not a conservative default someone should
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` now HAS verified semantics on Linux: it throws
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, which is exactly what produced the bug this replaced.
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 ("that inference is exactly what
18899
- * produced the bug this replaced"), and an earlier version of THIS comment did
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 was assumed for seven review rounds and
18917
- * is now also measured (real Credential Manager, real binding, throwaway service,
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 MR — the dev-machine probe is a point
18924
- * measurement, not a regression gate. (Tracked internally; this file ships to
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, both inherited from siblings that already paid for them:
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
- * The old code regenerated it, which converts one anomaly into every secret
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, and
19151
- * that is the opposite of what an earlier version concluded. The reasoning that
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 this used to serve is not lost: an install with nothing
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 — and an earlier version of this comment
19281
- // claimed it did, with a "verified": the library marks a lock released when
19282
- // it DETECTS a compromise, and then our release below is a no-op that cannot
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 — and the previous version of this comment was
19297
- // WRONG, so trust the numbers below and not the prose. It claimed reaching a
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 (probes recorded in the MR):
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. The hand-rolled lock this replaced serialised the break
19317
- // behind a second mutex — the right idea — and that mutex then age-broke a LIVE
19318
- // breaker at 10s with no liveness check: `maxOccupancy = 2`, measured with no
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 — and an earlier
19450
- * version said exactly that and cast the `undefined` away. It was wrong:
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 now only the
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 (an earlier version promised "PROVED gone") hid a real residual:
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) shipped, and made an unrelated
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. Two reviewers read this as dead code. It is not — it is UNEXERCISED, and
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. An
19795
- * earlier version gated on reachability and shipped the bug anyway, because a
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 fired, the delete quietly failed, the row was
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 (an earlier version
19813
- * claimed it was locked; the correction then overshot to "nothing here reads
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, and that is the part an earlier
19966
- * version got wrong. It said failure "simply leaves the secret where it was" while
19967
- * three abort paths returned after `setSecret` had already landed a SECOND copy in
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: two of the three leaking paths already had a hand-written cleanup and
19975
- * the other two did not, which is precisely the shape of bug that keeps costing this
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
- "No bridge token: set VECTROS_BOOTSTRAP_TOKEN, pass --token, or run `vectros login` first (required in non-interactive mode)."
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("\n Identities to ensure (tenant-wide, idempotent by external id):\n");
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("\n Identities to ensure (tenant-wide, idempotent by external id):\n");
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 app-context and the principal lookup had run; no grant or anything else was changed.
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) and profiles:c:self (create a profile for yourself only \u2014 the
22335
- # platform resolves "self" from the caller, not from anything you declare here). A bare
22336
- # profiles:c, or profiles:c:usr_<id> naming another principal, is HARD-REJECTED \u2014 by
22337
- # design, no override. Same for keys/users/admin/namespaces/*.
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' is admitted
22354
- # today; every other name is HARD-REJECTED, same posture as the scope gate above.
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 \u2014 a forked or hand-written blueprint can
22442
- never request control-plane access.
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("\n Identities to ensure (tenant-wide, idempotent by external id):\n");
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 resolveSelfPrincipal(auth.client, auth.cognitoToken);
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 = self2 ? { ...identity, userId: self2.userId, userPrincipalId: self2.principalId } : identity;
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 resolveSelfPrincipal(auth.client, auth.cognitoToken);
23472
- if (!self2) {
23473
- throw new Error(
23474
- "Could not resolve your own user id from your login. Pass --principal usr_<id> explicitly (find the id with `vectros identity list --type user`)."
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
- roleIds: opts.role,
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 resolveSelfPrincipal(auth.client, auth.cognitoToken);
24159
+ const self2 = await resolveSelfPrincipalForTenant(
24160
+ { ...auth, client: auth.client },
24161
+ deps.fetchImpl
24162
+ );
23869
24163
  if (!self2) {
23870
- process.stderr.write(
23871
- " \u2716 Could not resolve your own user id from your login. Pass --principal usr_<id> explicitly.\n"
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 = ["PLATFORM_BILLED", "CUSTOMER_DIRECT"];
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
- if (!BILLING_MODES.includes(opts.billingMode)) {
24889
- process.stderr.write(` \u2716 ${invalidEnum("--billing-mode", opts.billingMode, BILLING_MODES)}
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
- if (opts.billingMode !== void 0 && !BILLING_MODES.includes(opts.billingMode)) {
25019
- process.stderr.write(` \u2716 ${invalidEnum("--billing-mode", opts.billingMode, BILLING_MODES)}
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 resolveSelfPrincipal(auth.client, auth.cognitoToken);
26634
- if (!self2) {
26635
- throw new Error(
26636
- "Could not resolve your own user id from your login. Pass --as usr_<id> explicitly (find it with `vectros identity list --type user`)."
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
- roleIds: opts.role,
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.24.0" : "dev",
26807
- sdk: true ? "0.46.0" : "dev",
26808
- blueprints: true ? "0.20.1" : "dev"
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
- process.exitCode = await runWhoami(toCommonOptions(flags), deps);
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 (e.g. byo-model-inference:customer-key)").requiredOption("--billing-mode <mode>", "'PLATFORM_BILLED' or 'CUSTOMER_DIRECT'").option("--platform-token-factor <n>", "required when --billing-mode PLATFORM_BILLED; refused otherwise").option("--platform-output-weight <n>", "required when --billing-mode PLATFORM_BILLED; refused otherwise").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)");
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 (still unique per tenant)").option("--provider-type <type>", "'ANTHROPIC_NATIVE' or 'OPENAI_COMPATIBLE'").option("--endpoint <url>", "update the provider endpoint").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").option("--waiver-type <type>", "update the waiver code this config is entitled under").option("--billing-mode <mode>", "'PLATFORM_BILLED' or 'CUSTOMER_DIRECT' \u2014 switching to CUSTOMER_DIRECT clears the platform pricing fields server-side").option("--platform-token-factor <n>", "update the platform pricing factor (PLATFORM_BILLED only)").option("--platform-output-weight <n>", "update the platform pricing weight (PLATFORM_BILLED only)").option("--status <status>", "'ACTIVE' or 'DISABLED' \u2014 activating requires a granted waiver for this config's waiverType");
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 principals (users + namespaced entities) \u2014 reference, never embed.");
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
- process.exitCode = await runIdentityGet({ ...toCommonOptions(flags), type: flags.type, id: flags.id }, deps);
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
- process.exitCode = await runIdentityDelete({ ...toCommonOptions(flags), type: flags.type, id: flags.id }, deps);
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,