@vectros-ai/cli 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +72 -5
- package/dist/cli.js +693 -31
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +693 -31
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -518,7 +518,7 @@ var require_graceful_fs = __commonJS({
|
|
|
518
518
|
function patch(fs5) {
|
|
519
519
|
polyfills(fs5);
|
|
520
520
|
fs5.gracefulify = patch;
|
|
521
|
-
fs5.createReadStream =
|
|
521
|
+
fs5.createReadStream = createReadStream3;
|
|
522
522
|
fs5.createWriteStream = createWriteStream;
|
|
523
523
|
var fs$readFile = fs5.readFile;
|
|
524
524
|
fs5.readFile = readFile4;
|
|
@@ -728,7 +728,7 @@ var require_graceful_fs = __commonJS({
|
|
|
728
728
|
}
|
|
729
729
|
});
|
|
730
730
|
}
|
|
731
|
-
function
|
|
731
|
+
function createReadStream3(path4, options) {
|
|
732
732
|
return new fs5.ReadStream(path4, options);
|
|
733
733
|
}
|
|
734
734
|
function createWriteStream(path4, options) {
|
|
@@ -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
|
-
//
|
|
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)
|
|
@@ -1634,6 +1634,7 @@ __export(api_exports, {
|
|
|
1634
1634
|
ContentTooLargeError: () => ContentTooLargeError,
|
|
1635
1635
|
DocumentContextEvent: () => DocumentContextEvent,
|
|
1636
1636
|
DocumentLookupRequest: () => DocumentLookupRequest,
|
|
1637
|
+
DocumentPatchRequest: () => DocumentPatchRequest,
|
|
1637
1638
|
DocumentRequest: () => DocumentRequest,
|
|
1638
1639
|
DocumentResponse: () => DocumentResponse,
|
|
1639
1640
|
DoneEvent: () => DoneEvent,
|
|
@@ -1977,6 +1978,13 @@ var DocumentLookupRequest;
|
|
|
1977
1978
|
Desc: "desc"
|
|
1978
1979
|
};
|
|
1979
1980
|
})(DocumentLookupRequest || (DocumentLookupRequest = {}));
|
|
1981
|
+
var DocumentPatchRequest;
|
|
1982
|
+
((DocumentPatchRequest2) => {
|
|
1983
|
+
DocumentPatchRequest2.Status = {
|
|
1984
|
+
Active: "ACTIVE",
|
|
1985
|
+
Archived: "ARCHIVED"
|
|
1986
|
+
};
|
|
1987
|
+
})(DocumentPatchRequest || (DocumentPatchRequest = {}));
|
|
1980
1988
|
var FileUploadRequest;
|
|
1981
1989
|
((FileUploadRequest2) => {
|
|
1982
1990
|
FileUploadRequest2.IndexMode = {
|
|
@@ -1989,6 +1997,7 @@ var FileUploadRequest;
|
|
|
1989
1997
|
var documents_exports = {};
|
|
1990
1998
|
__export(documents_exports, {
|
|
1991
1999
|
DocumentLookupRequest: () => DocumentLookupRequest,
|
|
2000
|
+
DocumentPatchRequest: () => DocumentPatchRequest,
|
|
1992
2001
|
FileUploadRequest: () => FileUploadRequest,
|
|
1993
2002
|
LookupDocumentsRequestOrder: () => LookupDocumentsRequestOrder
|
|
1994
2003
|
});
|
|
@@ -4653,6 +4662,7 @@ var AuthClient = class {
|
|
|
4653
4662
|
* @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
4654
4663
|
*
|
|
4655
4664
|
* @throws {@link Vectros.BadRequestError}
|
|
4665
|
+
* @throws {@link Vectros.PaymentRequiredError}
|
|
4656
4666
|
* @throws {@link Vectros.ForbiddenError}
|
|
4657
4667
|
* @throws {@link Vectros.TooManyRequestsError}
|
|
4658
4668
|
*
|
|
@@ -4702,6 +4712,8 @@ var AuthClient = class {
|
|
|
4702
4712
|
switch (_response.error.statusCode) {
|
|
4703
4713
|
case 400:
|
|
4704
4714
|
throw new BadRequestError(_response.error.body, _response.rawResponse);
|
|
4715
|
+
case 402:
|
|
4716
|
+
throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
|
|
4705
4717
|
case 403:
|
|
4706
4718
|
throw new ForbiddenError(_response.error.body, _response.rawResponse);
|
|
4707
4719
|
case 429:
|
|
@@ -6547,7 +6559,7 @@ var ComplianceClient = class {
|
|
|
6547
6559
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
6548
6560
|
}
|
|
6549
6561
|
/**
|
|
6550
|
-
* 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
|
|
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.
|
|
6551
6563
|
*
|
|
6552
6564
|
* @param {Vectros.ErasureRequest} request
|
|
6553
6565
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6610,7 +6622,7 @@ var ComplianceClient = class {
|
|
|
6610
6622
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/erasure-requests");
|
|
6611
6623
|
}
|
|
6612
6624
|
/**
|
|
6613
|
-
* 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, and reports of dangling references and shared rows
|
|
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.
|
|
6614
6626
|
*
|
|
6615
6627
|
* @param {Vectros.GetErasureRequestRequest} request
|
|
6616
6628
|
* @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6853,7 +6865,7 @@ var DocumentsClient = class {
|
|
|
6853
6865
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents");
|
|
6854
6866
|
}
|
|
6855
6867
|
/**
|
|
6856
|
-
* Creates a document from a raw text string and queues it for asynchronous indexing so it becomes searchable. Optionally supply an `externalId` to make the create idempotent — if a document with the same `externalId` already exists in your context, that existing document is returned unchanged instead of a duplicate being created. The response's `created` field (and the HTTP status — 201 when created, 200 when an existing document was returned) tells the two apart. To overwrite an existing document's content instead of returning it unchanged, set `?upsert=true` (this also requires the `documents:u` scope). Requires the `documents:c` scope to create. Being returned the existing document on a collision is a read of that document's data and additionally requires the `documents:r` scope — a credential holding `documents:c` alone receives a `400` ("already exists") on collision instead of the document.
|
|
6868
|
+
* Creates a document from a raw text string and queues it for asynchronous indexing so it becomes searchable. Optionally supply an `externalId` to make the create idempotent — if a document with the same `externalId` already exists in your context, that existing document is returned unchanged instead of a duplicate being created. The response's `created` field (and the HTTP status — 201 when created, 200 when an existing document was returned) tells the two apart. To overwrite an existing document's content instead of returning it unchanged, set `?upsert=true` (this also requires the `documents:u` scope). Requires the `documents:c` scope to create. Being returned the existing document on a collision is a read of that document's data and additionally requires the `documents:r` scope — a credential holding `documents:c` alone receives a `400` ("already exists") on collision instead of the document. Supplying `externalId` WITHOUT `schemaId` is refused (`400`) unless you also pass `confirmUntyped=true` — externalId uniqueness is scoped per schema, so omitting `schemaId` by mistake would otherwise silently create a separate untyped document instead of targeting the typed one you likely meant.
|
|
6857
6869
|
*
|
|
6858
6870
|
* @param {Vectros.IngestDocumentRequest} request
|
|
6859
6871
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -6873,10 +6885,11 @@ var DocumentsClient = class {
|
|
|
6873
6885
|
return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
|
|
6874
6886
|
}
|
|
6875
6887
|
async __ingestDocument(request, requestOptions) {
|
|
6876
|
-
const { upsert, allowClear, body: _body } = request;
|
|
6888
|
+
const { upsert, allowClear, confirmUntyped, body: _body } = request;
|
|
6877
6889
|
const _queryParams = {
|
|
6878
6890
|
upsert,
|
|
6879
|
-
allowClear
|
|
6891
|
+
allowClear,
|
|
6892
|
+
confirmUntyped
|
|
6880
6893
|
};
|
|
6881
6894
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
6882
6895
|
const _headers = mergeHeaders(
|
|
@@ -7111,7 +7124,7 @@ var DocumentsClient = class {
|
|
|
7111
7124
|
/**
|
|
7112
7125
|
* Partially updates a document using an RFC 7386 JSON Merge Patch. The `payload` object is deep-merged: keys you send overwrite existing values (recursing into nested objects), a key set to `null` is deleted, and keys you omit are preserved — unlike PUT, which replaces the whole payload. Top-level fields (`title`, `folderId`, `schemaId`, ownership) are set when present and left unchanged when omitted; sending a top-level field as `null` is rejected. Supplying `text` re-ingests the document body (same as PUT). `indexMode`, `externalId`, and `storeText` (text retention is fixed at ingest) are immutable and rejected if present. The merged result is validated against the bound schema. Pass `expectedVersion` for optimistic concurrency (409 on conflict). Requires the `documents:u` scope.
|
|
7113
7126
|
*
|
|
7114
|
-
* @param {Vectros.
|
|
7127
|
+
* @param {Vectros.DocumentPatchRequest} request
|
|
7115
7128
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
7116
7129
|
*
|
|
7117
7130
|
* @throws {@link Vectros.BadRequestError}
|
|
@@ -7122,17 +7135,14 @@ var DocumentsClient = class {
|
|
|
7122
7135
|
*
|
|
7123
7136
|
* @example
|
|
7124
7137
|
* await client.documents.patchDocument({
|
|
7125
|
-
* id: "id"
|
|
7126
|
-
* body: {
|
|
7127
|
-
* title: "Patient Intake Form \u2014 Jane Doe"
|
|
7128
|
-
* }
|
|
7138
|
+
* id: "id"
|
|
7129
7139
|
* })
|
|
7130
7140
|
*/
|
|
7131
7141
|
patchDocument(request, requestOptions) {
|
|
7132
7142
|
return HttpResponsePromise.fromPromise(this.__patchDocument(request, requestOptions));
|
|
7133
7143
|
}
|
|
7134
7144
|
async __patchDocument(request, requestOptions) {
|
|
7135
|
-
const { id,
|
|
7145
|
+
const { id, ..._body } = request;
|
|
7136
7146
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7137
7147
|
const _headers = mergeHeaders(
|
|
7138
7148
|
_authRequest.headers,
|
|
@@ -7504,7 +7514,7 @@ var DocumentsClient = class {
|
|
|
7504
7514
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/versions");
|
|
7505
7515
|
}
|
|
7506
7516
|
/**
|
|
7507
|
-
* Starts a file-based document by returning a short-lived presigned S3 PUT URL. Upload the file bytes directly to `uploadUrl`; the document is then automatically queued for text extraction and asynchronous indexing. Supplying an `externalId` makes this idempotent — re-initiating an upload with the same `externalId` re-issues a fresh presigned URL for the SAME existing document, targeting a newly-staged object rather than the document's current one — your PUT lands there first, and the document only adopts it once the upload is validated (so a re-upload inherently replaces the file body) rather than creating a duplicate. The response's `created` field (and the HTTP status — 201 when a new document was minted, 200 when an existing one was matched) tells the two apart. With `?upsert=true`, the submitted `payload`/`title` are also applied to the matched document (file-body divergence cannot be diffed at upload-init — the bytes have not arrived yet — so the re-upload itself replaces the body). Creating a NEW document requires the `documents:c` scope. Re-uploading over an EXISTING document overwrites (and re-indexes) its body, so it is an update: it requires the `documents:u` scope (as does `?upsert=true` for the metadata). **Your PUT to `uploadUrl` MUST include the header named in the response's `requiredHeaderName`, set to `requiredHeaderValue` exactly.** It is a signed part of the presigned request — the PUT never reaches this service either way, so neither failure uses this API's JSON error shape. Omitting it, or sending a different value, invalidates the request's signature and S3 rejects it with a 403 `SignatureDoesNotMatch`. Sending it correctly makes the URL single-use: `requiredHeaderName` is always `If-None-Match` and `requiredHeaderValue` is always `*` — a create, a recovery upload for a document whose earlier file is missing, and a re-upload ALL target a freshly-minted, never-written key, so the same S3 conditional-write precondition applies uniformly (S3's own documented conditional-write behavior): a replay after the object already exists (from your own completed PUT, or anyone else's) fails with `412 Precondition Failed`; a replay after the object was deleted and never re-created finds an absent key again and can succeed — single-use is a property of the object's existence, not a one-time token. **Either precondition can also fail with `409 Conflict` if your PUT genuinely races a concurrent delete on the same object.** Whichever of `412` or `409` you receive, the remedy is the same: call this endpoint again for a fresh URL rather than retrying the same PUT — a fresh call always targets a new key, so it is never subject to whatever raced the previous one.
|
|
7517
|
+
* Starts a file-based document by returning a short-lived presigned S3 PUT URL. Upload the file bytes directly to `uploadUrl`; the document is then automatically queued for text extraction and asynchronous indexing. Supplying an `externalId` makes this idempotent — re-initiating an upload with the same `externalId` re-issues a fresh presigned URL for the SAME existing document, targeting a newly-staged object rather than the document's current one — your PUT lands there first, and the document only adopts it once the upload is validated (so a re-upload inherently replaces the file body) rather than creating a duplicate. The response's `created` field (and the HTTP status — 201 when a new document was minted, 200 when an existing one was matched) tells the two apart. With `?upsert=true`, the submitted `payload`/`title` are also applied to the matched document (file-body divergence cannot be diffed at upload-init — the bytes have not arrived yet — so the re-upload itself replaces the body). Creating a NEW document requires the `documents:c` scope. Re-uploading over an EXISTING document overwrites (and re-indexes) its body, so it is an update: it requires the `documents:u` scope (as does `?upsert=true` for the metadata). **Your PUT to `uploadUrl` MUST include the header named in the response's `requiredHeaderName`, set to `requiredHeaderValue` exactly.** It is a signed part of the presigned request — the PUT never reaches this service either way, so neither failure uses this API's JSON error shape. Omitting it, or sending a different value, invalidates the request's signature and S3 rejects it with a 403 `SignatureDoesNotMatch`. Sending it correctly makes the URL single-use: `requiredHeaderName` is always `If-None-Match` and `requiredHeaderValue` is always `*` — a create, a recovery upload for a document whose earlier file is missing, and a re-upload ALL target a freshly-minted, never-written key, so the same S3 conditional-write precondition applies uniformly (S3's own documented conditional-write behavior): a replay after the object already exists (from your own completed PUT, or anyone else's) fails with `412 Precondition Failed`; a replay after the object was deleted and never re-created finds an absent key again and can succeed — single-use is a property of the object's existence, not a one-time token. **Either precondition can also fail with `409 Conflict` if your PUT genuinely races a concurrent delete on the same object.** Whichever of `412` or `409` you receive, the remedy is the same: call this endpoint again for a fresh URL rather than retrying the same PUT — a fresh call always targets a new key, so it is never subject to whatever raced the previous one. Supplying `externalId` WITHOUT `schemaId` is refused (`400`) unless you also pass `confirmUntyped=true` — externalId uniqueness is scoped per schema, so omitting `schemaId` by mistake would otherwise silently create a separate untyped document instead of targeting the typed one you likely meant.
|
|
7508
7518
|
*
|
|
7509
7519
|
* @param {Vectros.FileUploadRequest} request
|
|
7510
7520
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7523,9 +7533,10 @@ var DocumentsClient = class {
|
|
|
7523
7533
|
return HttpResponsePromise.fromPromise(this.__uploadDocument(request, requestOptions));
|
|
7524
7534
|
}
|
|
7525
7535
|
async __uploadDocument(request, requestOptions) {
|
|
7526
|
-
const { upsert, ..._body } = request;
|
|
7536
|
+
const { upsert, confirmUntyped, ..._body } = request;
|
|
7527
7537
|
const _queryParams = {
|
|
7528
|
-
upsert
|
|
7538
|
+
upsert,
|
|
7539
|
+
confirmUntyped
|
|
7529
7540
|
};
|
|
7530
7541
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7531
7542
|
const _headers = mergeHeaders(
|
|
@@ -12541,7 +12552,11 @@ var BlueprintIssuerSchema = import_zod.z.object({
|
|
|
12541
12552
|
* actually scoped. An unverified domain fails the apply with the platform's `400`.
|
|
12542
12553
|
*
|
|
12543
12554
|
* Omit to register a domain-less issuer. Omitting it on a re-apply leaves an existing scope
|
|
12544
|
-
* unchanged
|
|
12555
|
+
* unchanged. On platform 0.45.0 and later a scope is removed only from a registration that
|
|
12556
|
+
* already holds the unrestricted `(issuer, audience)` pair, which one created with a domain
|
|
12557
|
+
* normally does not (`vectros issuers update --clear-restricted-to-domain` is refused for it):
|
|
12558
|
+
* replace it with a new issuer declared without `restrictedToDomain` and verify that, under a
|
|
12559
|
+
* different app context if the old issuer has served users.
|
|
12545
12560
|
*/
|
|
12546
12561
|
restrictedToDomain: import_zod.z.string().regex(RESTRICTED_DOMAIN_RE, {
|
|
12547
12562
|
message: "restrictedToDomain must be a lowercase DNS name such as 'acme.com' \u2014 letters, digits and dashes in dot-separated labels, at least two labels, no scheme, path, port or uppercase; write an internationalized domain in its punycode (xn--\u2026) form"
|
|
@@ -15655,7 +15670,7 @@ async function devRouteError(res, action) {
|
|
|
15655
15670
|
}
|
|
15656
15671
|
function formatCliError(err, opts) {
|
|
15657
15672
|
const info = extractServerError(err);
|
|
15658
|
-
const actionable = renderActionableError(info);
|
|
15673
|
+
const actionable = renderActionableError(info, opts.notFoundHint);
|
|
15659
15674
|
const block = actionable ?? ` \u2716 ${opts.action}: ${info.message}
|
|
15660
15675
|
`;
|
|
15661
15676
|
const debug = opts.debug ?? Boolean(process.env.VECTROS_MCP_DEBUG);
|
|
@@ -15669,7 +15684,7 @@ function formatCliError(err, opts) {
|
|
|
15669
15684
|
}
|
|
15670
15685
|
return block;
|
|
15671
15686
|
}
|
|
15672
|
-
function renderActionableError(info) {
|
|
15687
|
+
function renderActionableError(info, notFoundHint) {
|
|
15673
15688
|
const msg = info.message;
|
|
15674
15689
|
switch (info.errorCode) {
|
|
15675
15690
|
case "SESSION_REFRESH_REQUIRED":
|
|
@@ -15707,7 +15722,7 @@ function renderActionableError(info) {
|
|
|
15707
15722
|
}
|
|
15708
15723
|
if (info.statusCode === 404) {
|
|
15709
15724
|
return ` \u2716 ${msg}
|
|
15710
|
-
Check the id \u2014 'vectros context list' enumerates the contexts you can reach.
|
|
15725
|
+
Check the id \u2014 ${notFoundHint ?? "'vectros context list' enumerates the contexts you can reach."}
|
|
15711
15726
|
`;
|
|
15712
15727
|
}
|
|
15713
15728
|
if (/migration-locked|cannot be (changed|modified)|immutable/i.test(msg) && /schema|field|lookup|type/i.test(msg)) {
|
|
@@ -19265,8 +19280,8 @@ async function withIndexLock(dir, fn, timings = LOCK_OPTS) {
|
|
|
19265
19280
|
// sections are ~1ms, so in practice the holder is gone before the first tick
|
|
19266
19281
|
// and nothing is ever detected. (The probe that observed the no-op ran at
|
|
19267
19282
|
// stale:2000 with the section artificially held open — a configuration we do
|
|
19268
|
-
// not ship
|
|
19269
|
-
//
|
|
19283
|
+
// not ship, and a measurement taken in a configuration you don't ship is not
|
|
19284
|
+
// evidence about the configuration you do.) The
|
|
19270
19285
|
// honest shape: our release runs unconditionally, and after a stale-break
|
|
19271
19286
|
// collision it CAN remove a successor's fresh lock — one more route to two
|
|
19272
19287
|
// writers at once, on top of the rmdir race below. The backstop for both is
|
|
@@ -24308,12 +24323,12 @@ function parseRestrictedToDomain(raw) {
|
|
|
24308
24323
|
const domain = raw.trim();
|
|
24309
24324
|
if (domain.length === 0) {
|
|
24310
24325
|
throw new Error(
|
|
24311
|
-
"--restricted-to-domain must be a non-blank domain (e.g. 'acme.com'). To remove the scope from an existing issuer, use --clear-restricted-to-domain."
|
|
24326
|
+
"--restricted-to-domain must be a non-blank domain (e.g. 'acme.com'). To remove the scope from an existing issuer, use --clear-restricted-to-domain (the platform refuses that unless the registration already holds the unrestricted (issuer, audience) pair)."
|
|
24312
24327
|
);
|
|
24313
24328
|
}
|
|
24314
24329
|
if (!isRestrictedToDomainName(domain.toLowerCase())) {
|
|
24315
24330
|
throw new Error(
|
|
24316
|
-
`--restricted-to-domain must be a DNS name such as 'acme.com' \u2014 letters, digits and dashes in dot-separated labels, at least two labels, no scheme, path or port (write an internationalized domain in its punycode xn-- form); got '${domain}'. To remove the scope from an existing issuer, use --clear-restricted-to-domain.`
|
|
24331
|
+
`--restricted-to-domain must be a DNS name such as 'acme.com' \u2014 letters, digits and dashes in dot-separated labels, at least two labels, no scheme, path or port (write an internationalized domain in its punycode xn-- form); got '${domain}'. To remove the scope from an existing issuer, use --clear-restricted-to-domain (the platform refuses that unless the registration already holds the unrestricted (issuer, audience) pair).`
|
|
24317
24332
|
);
|
|
24318
24333
|
}
|
|
24319
24334
|
return domain;
|
|
@@ -24766,6 +24781,541 @@ ${renderIssuer(issuer)}
|
|
|
24766
24781
|
});
|
|
24767
24782
|
}
|
|
24768
24783
|
|
|
24784
|
+
// src/commands/provider-config.ts
|
|
24785
|
+
var import_node_fs5 = require("fs");
|
|
24786
|
+
var readline6 = __toESM(require("readline/promises"));
|
|
24787
|
+
var PROVIDER_TYPES = ["ANTHROPIC_NATIVE", "OPENAI_COMPATIBLE"];
|
|
24788
|
+
var BILLING_MODES = ["PLATFORM_BILLED", "CUSTOMER_DIRECT"];
|
|
24789
|
+
var STATUSES = ["ACTIVE", "DISABLED"];
|
|
24790
|
+
var MAX_SECRET_BYTES = 8 * 1024;
|
|
24791
|
+
async function readBoundedSecret(stream) {
|
|
24792
|
+
try {
|
|
24793
|
+
return await readAllFrom(stream, MAX_SECRET_BYTES);
|
|
24794
|
+
} catch (err) {
|
|
24795
|
+
if (err instanceof TokenTooLargeError) {
|
|
24796
|
+
throw new Error(
|
|
24797
|
+
`the input is larger than ${MAX_SECRET_BYTES / 1024} KB, which no API key is; nothing was read further and nothing was sent.`
|
|
24798
|
+
);
|
|
24799
|
+
}
|
|
24800
|
+
throw err;
|
|
24801
|
+
}
|
|
24802
|
+
}
|
|
24803
|
+
function fileFailureReason2(err) {
|
|
24804
|
+
switch (err?.code) {
|
|
24805
|
+
case "ENOENT":
|
|
24806
|
+
return "no such file.";
|
|
24807
|
+
case "EISDIR":
|
|
24808
|
+
return "that is a directory.";
|
|
24809
|
+
case "EACCES":
|
|
24810
|
+
case "EPERM":
|
|
24811
|
+
return "permission denied.";
|
|
24812
|
+
default:
|
|
24813
|
+
return "it could not be read.";
|
|
24814
|
+
}
|
|
24815
|
+
}
|
|
24816
|
+
async function resolveApiKeySecret(fileFlag, deps, mode) {
|
|
24817
|
+
const isTty = deps.isTty ?? Boolean(process.stdin.isTTY);
|
|
24818
|
+
const readStdin = deps.readStdin ?? (() => readBoundedSecret(process.stdin));
|
|
24819
|
+
const readTextFile = deps.readTextFile ?? ((path4) => readBoundedSecret((0, import_node_fs5.createReadStream)(path4)));
|
|
24820
|
+
let raw;
|
|
24821
|
+
if (fileFlag !== void 0 && fileFlag !== "-") {
|
|
24822
|
+
if (looksLikeCredential(fileFlag)) {
|
|
24823
|
+
throw new Error(
|
|
24824
|
+
"--api-key-secret-file takes the PATH of a file holding the secret, but the value given looks like a secret (or is too long to be a path). Save it to a file and pass that path (for a file in this directory, write ./name), or pipe it on stdin with --api-key-secret-file -."
|
|
24825
|
+
);
|
|
24826
|
+
}
|
|
24827
|
+
try {
|
|
24828
|
+
raw = await readTextFile(fileFlag);
|
|
24829
|
+
} catch (err) {
|
|
24830
|
+
const name = safeBaseName(fileFlag);
|
|
24831
|
+
throw new Error(
|
|
24832
|
+
`could not read the file given to --api-key-secret-file${name ? ` (${name})` : ""}: ${fileFailureReason2(err)}`
|
|
24833
|
+
);
|
|
24834
|
+
}
|
|
24835
|
+
} else if (fileFlag === "-") {
|
|
24836
|
+
if (isTty) {
|
|
24837
|
+
throw new Error(
|
|
24838
|
+
"stdin is a terminal, so --api-key-secret-file - would have you paste the secret where it is displayed. Pipe it instead (some-command | vectros provider-config \u2026), or save it to a file and pass --api-key-secret-file <path>."
|
|
24839
|
+
);
|
|
24840
|
+
}
|
|
24841
|
+
raw = await readStdin();
|
|
24842
|
+
} else if (mode === "required" && !isTty) {
|
|
24843
|
+
process.stderr.write(" \u2026 no --api-key-secret-file given: reading the secret from stdin.\n");
|
|
24844
|
+
raw = await readStdin();
|
|
24845
|
+
} else if (mode === "required") {
|
|
24846
|
+
throw new Error(
|
|
24847
|
+
"apiKeySecret is required. Save it to a file and pass --api-key-secret-file <path>, or pipe it on stdin (some-command | vectros provider-config create \u2026)."
|
|
24848
|
+
);
|
|
24849
|
+
} else {
|
|
24850
|
+
return void 0;
|
|
24851
|
+
}
|
|
24852
|
+
const secret = raw.trim();
|
|
24853
|
+
if (secret.length === 0) throw new Error("the secret is empty.");
|
|
24854
|
+
return secret;
|
|
24855
|
+
}
|
|
24856
|
+
function renderProviderConfig(c) {
|
|
24857
|
+
return formatKeyValues([
|
|
24858
|
+
["id", c.id],
|
|
24859
|
+
["tenantId", c.tenantId],
|
|
24860
|
+
["alias", c.alias],
|
|
24861
|
+
["providerType", c.providerType],
|
|
24862
|
+
["endpoint", c.endpoint],
|
|
24863
|
+
["declaredProcessingRegion", c.declaredProcessingRegion],
|
|
24864
|
+
["defaultProviderModelId", c.defaultProviderModelId],
|
|
24865
|
+
["waiverType", c.waiverType],
|
|
24866
|
+
["billingMode", c.billingMode],
|
|
24867
|
+
["platformTokenFactor", c.platformTokenFactor],
|
|
24868
|
+
["platformOutputWeight", c.platformOutputWeight],
|
|
24869
|
+
["status", c.status],
|
|
24870
|
+
["createdAt", c.createdAt]
|
|
24871
|
+
]);
|
|
24872
|
+
}
|
|
24873
|
+
function invalidEnum(flag, value, allowed) {
|
|
24874
|
+
return `${flag} must be one of ${allowed.join(", ")} (got '${value}').`;
|
|
24875
|
+
}
|
|
24876
|
+
var NOT_FOUND_HINT = "'vectros provider-config list --tenant <test|live>' enumerates configs in a tenant.";
|
|
24877
|
+
async function runProviderConfigCreate(opts, deps = {}) {
|
|
24878
|
+
if (!PROVIDER_TYPES.includes(opts.providerType)) {
|
|
24879
|
+
process.stderr.write(` \u2716 ${invalidEnum("--provider-type", opts.providerType, PROVIDER_TYPES)}
|
|
24880
|
+
`);
|
|
24881
|
+
return 2;
|
|
24882
|
+
}
|
|
24883
|
+
if (!BILLING_MODES.includes(opts.billingMode)) {
|
|
24884
|
+
process.stderr.write(` \u2716 ${invalidEnum("--billing-mode", opts.billingMode, BILLING_MODES)}
|
|
24885
|
+
`);
|
|
24886
|
+
return 2;
|
|
24887
|
+
}
|
|
24888
|
+
if (opts.status !== void 0 && !STATUSES.includes(opts.status)) {
|
|
24889
|
+
process.stderr.write(` \u2716 ${invalidEnum("--status", opts.status, STATUSES)}
|
|
24890
|
+
`);
|
|
24891
|
+
return 2;
|
|
24892
|
+
}
|
|
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
|
+
let apiKeySecret;
|
|
24907
|
+
try {
|
|
24908
|
+
apiKeySecret = await resolveApiKeySecret(opts.apiKeySecretFile, deps, "required");
|
|
24909
|
+
} catch (err) {
|
|
24910
|
+
process.stderr.write(` \u2716 ${formatError(err)}
|
|
24911
|
+
`);
|
|
24912
|
+
return 2;
|
|
24913
|
+
}
|
|
24914
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
24915
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
24916
|
+
try {
|
|
24917
|
+
const tenantId = requireTenantId(auth);
|
|
24918
|
+
const res = await fetchImpl(`${auth.baseUrl.replace(/\/$/, "")}/developer/provider-configs`, {
|
|
24919
|
+
method: "POST",
|
|
24920
|
+
headers: { Authorization: `Bearer ${auth.cognitoToken}`, "Content-Type": "application/json" },
|
|
24921
|
+
body: JSON.stringify({
|
|
24922
|
+
tenantId,
|
|
24923
|
+
alias: opts.alias,
|
|
24924
|
+
providerType: opts.providerType,
|
|
24925
|
+
endpoint: opts.endpoint,
|
|
24926
|
+
declaredProcessingRegion: opts.declaredProcessingRegion,
|
|
24927
|
+
defaultProviderModelId: opts.defaultProviderModelId,
|
|
24928
|
+
apiKeySecret,
|
|
24929
|
+
waiverType: opts.waiverType,
|
|
24930
|
+
billingMode: opts.billingMode,
|
|
24931
|
+
platformTokenFactor: opts.platformTokenFactor,
|
|
24932
|
+
platformOutputWeight: opts.platformOutputWeight,
|
|
24933
|
+
status: opts.status
|
|
24934
|
+
})
|
|
24935
|
+
});
|
|
24936
|
+
if (!res.ok) throw await devRouteError(res, "provider-config create");
|
|
24937
|
+
const config = await res.json();
|
|
24938
|
+
if (opts.json) {
|
|
24939
|
+
process.stdout.write(`${formatJson(config)}
|
|
24940
|
+
`);
|
|
24941
|
+
return 0;
|
|
24942
|
+
}
|
|
24943
|
+
process.stdout.write(`
|
|
24944
|
+
\u2714 provider config '${opts.alias}' created
|
|
24945
|
+
${renderProviderConfig(config)}
|
|
24946
|
+
`);
|
|
24947
|
+
return 0;
|
|
24948
|
+
} catch (err) {
|
|
24949
|
+
process.stderr.write(formatCliError(err, { action: "provider-config create failed" }));
|
|
24950
|
+
return 1;
|
|
24951
|
+
}
|
|
24952
|
+
});
|
|
24953
|
+
}
|
|
24954
|
+
async function runProviderConfigList(opts, deps = {}) {
|
|
24955
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
24956
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
24957
|
+
try {
|
|
24958
|
+
const tenantId = requireTenantId(auth);
|
|
24959
|
+
const url = `${auth.baseUrl.replace(/\/$/, "")}/developer/provider-configs?tenantId=${encodeURIComponent(tenantId)}`;
|
|
24960
|
+
const res = await fetchImpl(url, { method: "GET", headers: { Authorization: `Bearer ${auth.cognitoToken}` } });
|
|
24961
|
+
if (!res.ok) throw await devRouteError(res, "provider-config list");
|
|
24962
|
+
const rows = await res.json();
|
|
24963
|
+
if (opts.json) {
|
|
24964
|
+
process.stdout.write(`${formatJson(rows)}
|
|
24965
|
+
`);
|
|
24966
|
+
return 0;
|
|
24967
|
+
}
|
|
24968
|
+
process.stdout.write(
|
|
24969
|
+
"\n" + formatTable(rows, [
|
|
24970
|
+
{ header: "id", get: (c) => c.id },
|
|
24971
|
+
{ header: "alias", get: (c) => c.alias },
|
|
24972
|
+
{ header: "provider type", get: (c) => c.providerType },
|
|
24973
|
+
{ header: "billing mode", get: (c) => c.billingMode },
|
|
24974
|
+
{ header: "status", get: (c) => c.status }
|
|
24975
|
+
]) + "\n"
|
|
24976
|
+
);
|
|
24977
|
+
return 0;
|
|
24978
|
+
} catch (err) {
|
|
24979
|
+
process.stderr.write(formatCliError(err, { action: "provider-config list failed" }));
|
|
24980
|
+
return 1;
|
|
24981
|
+
}
|
|
24982
|
+
});
|
|
24983
|
+
}
|
|
24984
|
+
async function runProviderConfigGet(opts, deps = {}) {
|
|
24985
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
24986
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
24987
|
+
try {
|
|
24988
|
+
const url = `${auth.baseUrl.replace(/\/$/, "")}/developer/provider-configs/${encodeURIComponent(opts.id)}`;
|
|
24989
|
+
const res = await fetchImpl(url, { method: "GET", headers: { Authorization: `Bearer ${auth.cognitoToken}` } });
|
|
24990
|
+
if (!res.ok) throw await devRouteError(res, "provider-config get");
|
|
24991
|
+
const config = await res.json();
|
|
24992
|
+
if (opts.json) {
|
|
24993
|
+
process.stdout.write(`${formatJson(config)}
|
|
24994
|
+
`);
|
|
24995
|
+
return 0;
|
|
24996
|
+
}
|
|
24997
|
+
process.stdout.write(`
|
|
24998
|
+
${renderProviderConfig(config)}
|
|
24999
|
+
`);
|
|
25000
|
+
return 0;
|
|
25001
|
+
} catch (err) {
|
|
25002
|
+
process.stderr.write(formatCliError(err, { action: "provider-config get failed", notFoundHint: NOT_FOUND_HINT }));
|
|
25003
|
+
return 1;
|
|
25004
|
+
}
|
|
25005
|
+
});
|
|
25006
|
+
}
|
|
25007
|
+
async function runProviderConfigUpdate(opts, deps = {}) {
|
|
25008
|
+
if (opts.providerType !== void 0 && !PROVIDER_TYPES.includes(opts.providerType)) {
|
|
25009
|
+
process.stderr.write(` \u2716 ${invalidEnum("--provider-type", opts.providerType, PROVIDER_TYPES)}
|
|
25010
|
+
`);
|
|
25011
|
+
return 2;
|
|
25012
|
+
}
|
|
25013
|
+
if (opts.billingMode !== void 0 && !BILLING_MODES.includes(opts.billingMode)) {
|
|
25014
|
+
process.stderr.write(` \u2716 ${invalidEnum("--billing-mode", opts.billingMode, BILLING_MODES)}
|
|
25015
|
+
`);
|
|
25016
|
+
return 2;
|
|
25017
|
+
}
|
|
25018
|
+
if (opts.status !== void 0 && !STATUSES.includes(opts.status)) {
|
|
25019
|
+
process.stderr.write(` \u2716 ${invalidEnum("--status", opts.status, STATUSES)}
|
|
25020
|
+
`);
|
|
25021
|
+
return 2;
|
|
25022
|
+
}
|
|
25023
|
+
let apiKeySecret;
|
|
25024
|
+
try {
|
|
25025
|
+
apiKeySecret = await resolveApiKeySecret(opts.apiKeySecretFile, deps, "optional");
|
|
25026
|
+
} catch (err) {
|
|
25027
|
+
process.stderr.write(` \u2716 ${formatError(err)}
|
|
25028
|
+
`);
|
|
25029
|
+
return 2;
|
|
25030
|
+
}
|
|
25031
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
25032
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
25033
|
+
try {
|
|
25034
|
+
const url = `${auth.baseUrl.replace(/\/$/, "")}/developer/provider-configs/${encodeURIComponent(opts.id)}`;
|
|
25035
|
+
const res = await fetchImpl(url, {
|
|
25036
|
+
method: "PUT",
|
|
25037
|
+
headers: { Authorization: `Bearer ${auth.cognitoToken}`, "Content-Type": "application/json" },
|
|
25038
|
+
body: JSON.stringify({
|
|
25039
|
+
alias: opts.alias,
|
|
25040
|
+
providerType: opts.providerType,
|
|
25041
|
+
endpoint: opts.endpoint,
|
|
25042
|
+
declaredProcessingRegion: opts.declaredProcessingRegion,
|
|
25043
|
+
defaultProviderModelId: opts.defaultProviderModelId,
|
|
25044
|
+
apiKeySecret,
|
|
25045
|
+
waiverType: opts.waiverType,
|
|
25046
|
+
billingMode: opts.billingMode,
|
|
25047
|
+
platformTokenFactor: opts.platformTokenFactor,
|
|
25048
|
+
platformOutputWeight: opts.platformOutputWeight,
|
|
25049
|
+
status: opts.status
|
|
25050
|
+
})
|
|
25051
|
+
});
|
|
25052
|
+
if (!res.ok) throw await devRouteError(res, "provider-config update");
|
|
25053
|
+
const config = await res.json();
|
|
25054
|
+
if (opts.json) {
|
|
25055
|
+
process.stdout.write(`${formatJson(config)}
|
|
25056
|
+
`);
|
|
25057
|
+
return 0;
|
|
25058
|
+
}
|
|
25059
|
+
process.stdout.write(`
|
|
25060
|
+
\u2714 provider config '${opts.id}' updated
|
|
25061
|
+
${renderProviderConfig(config)}
|
|
25062
|
+
`);
|
|
25063
|
+
return 0;
|
|
25064
|
+
} catch (err) {
|
|
25065
|
+
process.stderr.write(formatCliError(err, { action: "provider-config update failed", notFoundHint: NOT_FOUND_HINT }));
|
|
25066
|
+
return 1;
|
|
25067
|
+
}
|
|
25068
|
+
});
|
|
25069
|
+
}
|
|
25070
|
+
async function runProviderConfigDelete(opts, deps = {}) {
|
|
25071
|
+
const { id } = opts;
|
|
25072
|
+
if (!opts.force) {
|
|
25073
|
+
const isTty = deps.isTty ?? Boolean(process.stdin.isTTY);
|
|
25074
|
+
if (!isTty) {
|
|
25075
|
+
process.stderr.write(
|
|
25076
|
+
` \u2716 provider-config delete needs an interactive confirmation.
|
|
25077
|
+
Re-run with --force to delete '${id}' without a prompt.
|
|
25078
|
+
`
|
|
25079
|
+
);
|
|
25080
|
+
return 2;
|
|
25081
|
+
}
|
|
25082
|
+
const promptCtx = deps.createPrompt ? deps.createPrompt() : (() => {
|
|
25083
|
+
const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
|
|
25084
|
+
return { prompt: (q) => rl.question(q), close: () => rl.close() };
|
|
25085
|
+
})();
|
|
25086
|
+
try {
|
|
25087
|
+
process.stdout.write(
|
|
25088
|
+
`
|
|
25089
|
+
\u26A0 Deleting provider config '${id}' removes it permanently. Any inference call that resolves
|
|
25090
|
+
a provider by this config's alias starts failing immediately. To disable it instead (reversible),
|
|
25091
|
+
run \`vectros provider-config update ${id} --status DISABLED\`.
|
|
25092
|
+
|
|
25093
|
+
`
|
|
25094
|
+
);
|
|
25095
|
+
const typed = await promptCtx.prompt(` Type the config id ('${id}') to confirm: `);
|
|
25096
|
+
if (typed.trim() !== id) {
|
|
25097
|
+
process.stderr.write(` \u2716 Confirmation did not match \u2014 nothing deleted.
|
|
25098
|
+
`);
|
|
25099
|
+
return 1;
|
|
25100
|
+
}
|
|
25101
|
+
} finally {
|
|
25102
|
+
promptCtx.close();
|
|
25103
|
+
}
|
|
25104
|
+
}
|
|
25105
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
25106
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
25107
|
+
try {
|
|
25108
|
+
const url = `${auth.baseUrl.replace(/\/$/, "")}/developer/provider-configs/${encodeURIComponent(id)}`;
|
|
25109
|
+
const res = await fetchImpl(url, { method: "DELETE", headers: { Authorization: `Bearer ${auth.cognitoToken}` } });
|
|
25110
|
+
if (!res.ok) throw await devRouteError(res, "provider-config delete");
|
|
25111
|
+
if (opts.json) {
|
|
25112
|
+
process.stdout.write(`${formatJson({ id, deleted: true })}
|
|
25113
|
+
`);
|
|
25114
|
+
return 0;
|
|
25115
|
+
}
|
|
25116
|
+
process.stdout.write(`
|
|
25117
|
+
\u2714 provider config '${id}' deleted
|
|
25118
|
+
`);
|
|
25119
|
+
return 0;
|
|
25120
|
+
} catch (err) {
|
|
25121
|
+
process.stderr.write(formatCliError(err, { action: "provider-config delete failed", notFoundHint: NOT_FOUND_HINT }));
|
|
25122
|
+
return 1;
|
|
25123
|
+
}
|
|
25124
|
+
});
|
|
25125
|
+
}
|
|
25126
|
+
|
|
25127
|
+
// src/commands/domains.ts
|
|
25128
|
+
var readline7 = __toESM(require("readline/promises"));
|
|
25129
|
+
function renderDomainVerifyResult(d) {
|
|
25130
|
+
return formatKeyValues([
|
|
25131
|
+
["id", d.id],
|
|
25132
|
+
["domain", d.domain],
|
|
25133
|
+
["status", d.status]
|
|
25134
|
+
]);
|
|
25135
|
+
}
|
|
25136
|
+
var NOT_FOUND_HINT2 = "'vectros domains list' enumerates your registered domains.";
|
|
25137
|
+
async function runDomainsRegister(opts, deps = {}) {
|
|
25138
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
25139
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
25140
|
+
try {
|
|
25141
|
+
const res = await fetchImpl(`${auth.baseUrl.replace(/\/$/, "")}/developer/domains`, {
|
|
25142
|
+
method: "POST",
|
|
25143
|
+
headers: { Authorization: `Bearer ${auth.cognitoToken}`, "Content-Type": "application/json" },
|
|
25144
|
+
body: JSON.stringify({ domain: opts.domain })
|
|
25145
|
+
});
|
|
25146
|
+
if (res.status === 409) {
|
|
25147
|
+
const raw = await res.text().catch(() => "");
|
|
25148
|
+
let message = "Domain already registered";
|
|
25149
|
+
let existingId;
|
|
25150
|
+
let existingStatus;
|
|
25151
|
+
try {
|
|
25152
|
+
const parsed = JSON.parse(raw);
|
|
25153
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) message = parsed.message.trim();
|
|
25154
|
+
if (typeof parsed.id === "string") existingId = parsed.id;
|
|
25155
|
+
if (typeof parsed.status === "string") existingStatus = parsed.status;
|
|
25156
|
+
} catch {
|
|
25157
|
+
}
|
|
25158
|
+
if (opts.json) {
|
|
25159
|
+
process.stdout.write(`${formatJson({ message, id: existingId, status: existingStatus })}
|
|
25160
|
+
`);
|
|
25161
|
+
return 1;
|
|
25162
|
+
}
|
|
25163
|
+
process.stderr.write(
|
|
25164
|
+
` \u2716 ${message}${existingId ? ` (id: ${existingId}${existingStatus ? `, status: ${existingStatus}` : ""})` : ""}
|
|
25165
|
+
` + (existingStatus === "PENDING" && existingId ? ` Still pending verification. Run: vectros domains verify ${existingId}
|
|
25166
|
+
` : " Run `vectros domains list` to find it.\n")
|
|
25167
|
+
);
|
|
25168
|
+
return 1;
|
|
25169
|
+
}
|
|
25170
|
+
if (!res.ok) throw await devRouteError(res, "domains register");
|
|
25171
|
+
const result = await res.json();
|
|
25172
|
+
if (opts.json) {
|
|
25173
|
+
process.stdout.write(`${formatJson(result)}
|
|
25174
|
+
`);
|
|
25175
|
+
return 0;
|
|
25176
|
+
}
|
|
25177
|
+
process.stdout.write(
|
|
25178
|
+
`
|
|
25179
|
+
\u2714 domain '${result.domain ?? opts.domain}' registered (id: ${result.id ?? "?"}, status: ${result.status ?? "PENDING"})
|
|
25180
|
+
|
|
25181
|
+
${result.instructions ?? `Add a DNS TXT record: _vectros-verify.${result.domain ?? opts.domain} \u2192 "${result.verificationToken ?? ""}"`}
|
|
25182
|
+
|
|
25183
|
+
Once the record is published, run:
|
|
25184
|
+
vectros domains verify ${result.id ?? "<id>"}
|
|
25185
|
+
`
|
|
25186
|
+
);
|
|
25187
|
+
return 0;
|
|
25188
|
+
} catch (err) {
|
|
25189
|
+
process.stderr.write(formatCliError(err, { action: "domains register failed" }));
|
|
25190
|
+
return 1;
|
|
25191
|
+
}
|
|
25192
|
+
});
|
|
25193
|
+
}
|
|
25194
|
+
async function runDomainsList(opts, deps = {}) {
|
|
25195
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
25196
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
25197
|
+
try {
|
|
25198
|
+
const res = await fetchImpl(`${auth.baseUrl.replace(/\/$/, "")}/developer/domains`, {
|
|
25199
|
+
method: "GET",
|
|
25200
|
+
headers: { Authorization: `Bearer ${auth.cognitoToken}` }
|
|
25201
|
+
});
|
|
25202
|
+
if (!res.ok) throw await devRouteError(res, "domains list");
|
|
25203
|
+
const rows = await res.json();
|
|
25204
|
+
if (opts.json) {
|
|
25205
|
+
process.stdout.write(`${formatJson(rows)}
|
|
25206
|
+
`);
|
|
25207
|
+
return 0;
|
|
25208
|
+
}
|
|
25209
|
+
process.stdout.write(
|
|
25210
|
+
"\n" + formatTable(rows, [
|
|
25211
|
+
{ header: "id", get: (d) => d.id },
|
|
25212
|
+
{ header: "domain", get: (d) => d.domain },
|
|
25213
|
+
{ header: "status", get: (d) => d.status },
|
|
25214
|
+
{ header: "verified at", get: (d) => d.verifiedAt }
|
|
25215
|
+
]) + "\n"
|
|
25216
|
+
);
|
|
25217
|
+
return 0;
|
|
25218
|
+
} catch (err) {
|
|
25219
|
+
process.stderr.write(formatCliError(err, { action: "domains list failed" }));
|
|
25220
|
+
return 1;
|
|
25221
|
+
}
|
|
25222
|
+
});
|
|
25223
|
+
}
|
|
25224
|
+
async function runDomainsDelete(opts, deps = {}) {
|
|
25225
|
+
const { id } = opts;
|
|
25226
|
+
if (!opts.force) {
|
|
25227
|
+
const isTty = deps.isTty ?? Boolean(process.stdin.isTTY);
|
|
25228
|
+
if (!isTty) {
|
|
25229
|
+
process.stderr.write(
|
|
25230
|
+
` \u2716 domains delete needs an interactive confirmation.
|
|
25231
|
+
Re-run with --force to delete '${id}' without a prompt.
|
|
25232
|
+
`
|
|
25233
|
+
);
|
|
25234
|
+
return 2;
|
|
25235
|
+
}
|
|
25236
|
+
const promptCtx = deps.createPrompt ? deps.createPrompt() : (() => {
|
|
25237
|
+
const rl = readline7.createInterface({ input: process.stdin, output: process.stdout });
|
|
25238
|
+
return { prompt: (q) => rl.question(q), close: () => rl.close() };
|
|
25239
|
+
})();
|
|
25240
|
+
try {
|
|
25241
|
+
process.stdout.write(
|
|
25242
|
+
`
|
|
25243
|
+
\u26A0 Deleting domain '${id}' removes its proof-of-control record. The platform refuses this while
|
|
25244
|
+
any active webhook still references it \u2014 disable or delete those first if this fails.
|
|
25245
|
+
|
|
25246
|
+
`
|
|
25247
|
+
);
|
|
25248
|
+
const typed = await promptCtx.prompt(` Type the domain id ('${id}') to confirm: `);
|
|
25249
|
+
if (typed.trim() !== id) {
|
|
25250
|
+
process.stderr.write(` \u2716 Confirmation did not match \u2014 nothing deleted.
|
|
25251
|
+
`);
|
|
25252
|
+
return 1;
|
|
25253
|
+
}
|
|
25254
|
+
} finally {
|
|
25255
|
+
promptCtx.close();
|
|
25256
|
+
}
|
|
25257
|
+
}
|
|
25258
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
25259
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
25260
|
+
try {
|
|
25261
|
+
const res = await fetchImpl(`${auth.baseUrl.replace(/\/$/, "")}/developer/domains/${encodeURIComponent(id)}`, {
|
|
25262
|
+
method: "DELETE",
|
|
25263
|
+
headers: { Authorization: `Bearer ${auth.cognitoToken}` }
|
|
25264
|
+
});
|
|
25265
|
+
if (!res.ok) throw await devRouteError(res, "domains delete");
|
|
25266
|
+
if (opts.json) {
|
|
25267
|
+
process.stdout.write(`${formatJson({ id, deleted: true })}
|
|
25268
|
+
`);
|
|
25269
|
+
return 0;
|
|
25270
|
+
}
|
|
25271
|
+
process.stdout.write(`
|
|
25272
|
+
\u2714 domain '${id}' deleted
|
|
25273
|
+
`);
|
|
25274
|
+
return 0;
|
|
25275
|
+
} catch (err) {
|
|
25276
|
+
process.stderr.write(formatCliError(err, { action: "domains delete failed", notFoundHint: NOT_FOUND_HINT2 }));
|
|
25277
|
+
return 1;
|
|
25278
|
+
}
|
|
25279
|
+
});
|
|
25280
|
+
}
|
|
25281
|
+
async function runDomainsVerify(opts, deps = {}) {
|
|
25282
|
+
return withAuth({ ...opts, provisioning: true }, deps, async (auth) => {
|
|
25283
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
25284
|
+
try {
|
|
25285
|
+
const res = await fetchImpl(
|
|
25286
|
+
`${auth.baseUrl.replace(/\/$/, "")}/developer/domains/${encodeURIComponent(opts.id)}/verify`,
|
|
25287
|
+
{ method: "POST", headers: { Authorization: `Bearer ${auth.cognitoToken}` } }
|
|
25288
|
+
);
|
|
25289
|
+
if (!res.ok) throw await devRouteError(res, "domains verify");
|
|
25290
|
+
const result = await res.json();
|
|
25291
|
+
const verified = result.verified === true && result.status === "VERIFIED";
|
|
25292
|
+
if (opts.json) {
|
|
25293
|
+
process.stdout.write(`${formatJson(result)}
|
|
25294
|
+
`);
|
|
25295
|
+
return verified ? 0 : 1;
|
|
25296
|
+
}
|
|
25297
|
+
if (!verified) {
|
|
25298
|
+
process.stderr.write(
|
|
25299
|
+
` \u2716 domain '${result.domain ?? opts.id}' is not verified (status: ${result.status ?? "(none)"}).
|
|
25300
|
+
Check the DNS TXT record is published, allow time for it to propagate, then re-run this command.
|
|
25301
|
+
`
|
|
25302
|
+
);
|
|
25303
|
+
return 1;
|
|
25304
|
+
}
|
|
25305
|
+
process.stdout.write(
|
|
25306
|
+
`
|
|
25307
|
+
\u2714 domain '${result.domain ?? opts.id}' verified.
|
|
25308
|
+
${renderDomainVerifyResult(result)}
|
|
25309
|
+
`
|
|
25310
|
+
);
|
|
25311
|
+
return 0;
|
|
25312
|
+
} catch (err) {
|
|
25313
|
+
process.stderr.write(formatCliError(err, { action: "domains verify failed", notFoundHint: NOT_FOUND_HINT2 }));
|
|
25314
|
+
return 1;
|
|
25315
|
+
}
|
|
25316
|
+
});
|
|
25317
|
+
}
|
|
25318
|
+
|
|
24769
25319
|
// src/commands/namespace.ts
|
|
24770
25320
|
function renderNamespace(n) {
|
|
24771
25321
|
return formatKeyValues([
|
|
@@ -24870,7 +25420,7 @@ ${renderNamespace(ns)}
|
|
|
24870
25420
|
|
|
24871
25421
|
// src/commands/scripts.ts
|
|
24872
25422
|
var import_promises4 = require("fs/promises");
|
|
24873
|
-
var
|
|
25423
|
+
var readline8 = __toESM(require("readline/promises"));
|
|
24874
25424
|
var SOURCE_MAX_BYTES = 3e5;
|
|
24875
25425
|
var INPUT_CONTRACT_MAX_BYTES = 5e4;
|
|
24876
25426
|
var utf8Bytes2 = (s) => new TextEncoder().encode(s).length;
|
|
@@ -25089,7 +25639,7 @@ async function runScriptsDelete(opts, deps = {}) {
|
|
|
25089
25639
|
return 2;
|
|
25090
25640
|
}
|
|
25091
25641
|
const promptCtx = deps.createPrompt ? deps.createPrompt() : (() => {
|
|
25092
|
-
const rl =
|
|
25642
|
+
const rl = readline8.createInterface({ input: process.stdin, output: process.stdout });
|
|
25093
25643
|
return { prompt: (q) => rl.question(q), close: () => rl.close() };
|
|
25094
25644
|
})();
|
|
25095
25645
|
try {
|
|
@@ -26248,9 +26798,9 @@ function emitJoined(opts) {
|
|
|
26248
26798
|
|
|
26249
26799
|
// src/build-info.ts
|
|
26250
26800
|
var BUILD_INFO = {
|
|
26251
|
-
cli: true ? "0.
|
|
26252
|
-
sdk: true ? "0.
|
|
26253
|
-
blueprints: true ? "0.20.
|
|
26801
|
+
cli: true ? "0.24.0" : "dev",
|
|
26802
|
+
sdk: true ? "0.46.0" : "dev",
|
|
26803
|
+
blueprints: true ? "0.20.1" : "dev"
|
|
26254
26804
|
};
|
|
26255
26805
|
function formatBuildInfo() {
|
|
26256
26806
|
return `vectros ${BUILD_INFO.cli} (sdk ${BUILD_INFO.sdk}, blueprints ${BUILD_INFO.blueprints})`;
|
|
@@ -26287,6 +26837,18 @@ function parseTenantFlag(raw) {
|
|
|
26287
26837
|
}
|
|
26288
26838
|
return raw;
|
|
26289
26839
|
}
|
|
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
|
+
}
|
|
26290
26852
|
function toCommonOptions(flags) {
|
|
26291
26853
|
return {
|
|
26292
26854
|
env: flags.env,
|
|
@@ -26335,6 +26897,8 @@ function buildProgram(deps = {}) {
|
|
|
26335
26897
|
registerRoleCommands(program, deps);
|
|
26336
26898
|
registerContextCommands(program, deps);
|
|
26337
26899
|
registerIssuersCommands(program, deps);
|
|
26900
|
+
registerProviderConfigCommands(program, deps);
|
|
26901
|
+
registerDomainsCommands(program, deps);
|
|
26338
26902
|
registerNamespaceCommands(program, deps);
|
|
26339
26903
|
registerScriptsCommands(program, deps);
|
|
26340
26904
|
registerAssumeCommand(program, deps);
|
|
@@ -26545,7 +27109,10 @@ function registerIssuersCommands(program, deps) {
|
|
|
26545
27109
|
).option("--clear-captured-claims", "capture nothing beyond the email claim (sends an empty list; XOR --captured-claim)").option(
|
|
26546
27110
|
"--restricted-to-domain <domain>",
|
|
26547
27111
|
"scope the registration to a domain already verified for your account; a token with no matching 'hd' claim then stops matching \u2014 omit to leave unchanged. Refused on a registration that is pending verification: verify it first"
|
|
26548
|
-
).option(
|
|
27112
|
+
).option(
|
|
27113
|
+
"--clear-restricted-to-domain",
|
|
27114
|
+
"remove the domain scope (XOR --restricted-to-domain). Refused unless the registration already holds the unrestricted (issuer, audience) pair, which one created scoped to a domain normally does not; see the README"
|
|
27115
|
+
).option("--tenant <tenant>", "tenant the context lives in: 'test' or 'live' (default live)");
|
|
26549
27116
|
addCommonAuthOptions(update).action(
|
|
26550
27117
|
async (issuerId, flags) => {
|
|
26551
27118
|
const tenant = parseTenantFlag(flags.tenant);
|
|
@@ -26606,6 +27173,101 @@ function registerIssuersCommands(program, deps) {
|
|
|
26606
27173
|
}
|
|
26607
27174
|
);
|
|
26608
27175
|
}
|
|
27176
|
+
function registerProviderConfigCommands(program, deps) {
|
|
27177
|
+
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)");
|
|
27179
|
+
addCommonAuthOptions(create).action(
|
|
27180
|
+
async (flags) => {
|
|
27181
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27182
|
+
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
|
+
process.exitCode = await runProviderConfigCreate(
|
|
27188
|
+
{
|
|
27189
|
+
...toCommonOptions(flags),
|
|
27190
|
+
alias: flags.alias,
|
|
27191
|
+
providerType: flags.providerType,
|
|
27192
|
+
endpoint: flags.endpoint,
|
|
27193
|
+
declaredProcessingRegion: flags.declaredProcessingRegion,
|
|
27194
|
+
defaultProviderModelId: flags.defaultProviderModelId,
|
|
27195
|
+
apiKeySecretFile: flags.apiKeySecretFile,
|
|
27196
|
+
waiverType: flags.waiverType,
|
|
27197
|
+
billingMode: flags.billingMode,
|
|
27198
|
+
platformTokenFactor,
|
|
27199
|
+
platformOutputWeight,
|
|
27200
|
+
status: flags.status,
|
|
27201
|
+
tenant
|
|
27202
|
+
},
|
|
27203
|
+
deps
|
|
27204
|
+
);
|
|
27205
|
+
}
|
|
27206
|
+
);
|
|
27207
|
+
const list = providerConfig.command("list").description("List provider configs in one tenant.").option("--tenant <tenant>", "tenant to read: 'test' or 'live' (default live)");
|
|
27208
|
+
addCommonAuthOptions(list).action(async (flags) => {
|
|
27209
|
+
const tenant = parseTenantFlag(flags.tenant);
|
|
27210
|
+
if (tenant === TENANT_FLAG_INVALID) return;
|
|
27211
|
+
process.exitCode = await runProviderConfigList({ ...toCommonOptions(flags), tenant }, deps);
|
|
27212
|
+
});
|
|
27213
|
+
const get = providerConfig.command("get").description("Show one provider config's metadata (no secret \u2014 the platform never returns it).").argument("<id>");
|
|
27214
|
+
addCommonAuthOptions(get).action(async (id, flags) => {
|
|
27215
|
+
process.exitCode = await runProviderConfigGet({ ...toCommonOptions(flags), id }, deps);
|
|
27216
|
+
});
|
|
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");
|
|
27218
|
+
addCommonAuthOptions(update).action(
|
|
27219
|
+
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
|
+
process.exitCode = await runProviderConfigUpdate(
|
|
27225
|
+
{
|
|
27226
|
+
...toCommonOptions(flags),
|
|
27227
|
+
id,
|
|
27228
|
+
alias: flags.alias,
|
|
27229
|
+
providerType: flags.providerType,
|
|
27230
|
+
endpoint: flags.endpoint,
|
|
27231
|
+
declaredProcessingRegion: flags.declaredProcessingRegion,
|
|
27232
|
+
defaultProviderModelId: flags.defaultProviderModelId,
|
|
27233
|
+
apiKeySecretFile: flags.apiKeySecretFile,
|
|
27234
|
+
waiverType: flags.waiverType,
|
|
27235
|
+
billingMode: flags.billingMode,
|
|
27236
|
+
platformTokenFactor,
|
|
27237
|
+
platformOutputWeight,
|
|
27238
|
+
status: flags.status
|
|
27239
|
+
},
|
|
27240
|
+
deps
|
|
27241
|
+
);
|
|
27242
|
+
}
|
|
27243
|
+
);
|
|
27244
|
+
const del = providerConfig.command("delete").description("Delete a provider config.").argument("<id>").option("--force", "skip the interactive confirmation (required when stdin is not a TTY)");
|
|
27245
|
+
addCommonAuthOptions(del).action(async (id, flags) => {
|
|
27246
|
+
process.exitCode = await runProviderConfigDelete(
|
|
27247
|
+
{ ...toCommonOptions(flags), id, force: Boolean(flags.force) },
|
|
27248
|
+
deps
|
|
27249
|
+
);
|
|
27250
|
+
});
|
|
27251
|
+
}
|
|
27252
|
+
function registerDomainsCommands(program, deps) {
|
|
27253
|
+
const domains = program.command("domains").description("Manage account-level domain ownership verification (DNS TXT proof-of-control).");
|
|
27254
|
+
const register = domains.command("register").description("Register a domain and start DNS TXT verification (not idempotent \u2014 a repeat --domain 409s).").requiredOption("--domain <domain>", "the domain to prove control of (e.g. example.com)");
|
|
27255
|
+
addCommonAuthOptions(register).action(async (flags) => {
|
|
27256
|
+
process.exitCode = await runDomainsRegister({ ...toCommonOptions(flags), domain: flags.domain }, deps);
|
|
27257
|
+
});
|
|
27258
|
+
const list = domains.command("list").description("List domains registered for this account (both tenants share this list).");
|
|
27259
|
+
addCommonAuthOptions(list).action(async (flags) => {
|
|
27260
|
+
process.exitCode = await runDomainsList(toCommonOptions(flags), deps);
|
|
27261
|
+
});
|
|
27262
|
+
const del = domains.command("delete").description("Deregister a domain (refused while any active webhook still references it).").argument("<id>").option("--force", "skip the interactive confirmation (required when stdin is not a TTY)");
|
|
27263
|
+
addCommonAuthOptions(del).action(async (id, flags) => {
|
|
27264
|
+
process.exitCode = await runDomainsDelete({ ...toCommonOptions(flags), id, force: Boolean(flags.force) }, deps);
|
|
27265
|
+
});
|
|
27266
|
+
const verify = domains.command("verify").description("Perform one DNS TXT lookup and report VERIFIED/FAILED \u2014 always re-runnable, never polls.").argument("<id>");
|
|
27267
|
+
addCommonAuthOptions(verify).action(async (id, flags) => {
|
|
27268
|
+
process.exitCode = await runDomainsVerify({ ...toCommonOptions(flags), id }, deps);
|
|
27269
|
+
});
|
|
27270
|
+
}
|
|
26609
27271
|
function registerNamespaceCommands(program, deps) {
|
|
26610
27272
|
const namespace = program.command("namespace").description("Manage scope-namespace registrations (whether scope:<namespace> values resolve to identity entities).");
|
|
26611
27273
|
const register = namespace.command("register").description("Register a new scope namespace (NOT idempotent \u2014 a repeat name 400s).").argument("<name>", "namespace name: 2-32 chars, a lowercase letter first, then lowercase/digit/dash/underscore").requiredOption("--context <c>", "app context to own this registration").requiredOption("--specificity-rank <n>", "position in the account specificity order (integer 0-1000000, unique)").option("--entity-backed", "values must resolve to an identity entity (create via `identity create --type <name>`)").option("--default-schema-id <id>", "default record schema bound to entities in this namespace");
|