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