@vectros-ai/cli 0.6.4 → 0.6.6
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 +27 -0
- package/dist/cli.js +200 -67
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +200 -67
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,33 @@
|
|
|
3
3
|
All notable changes to `@vectros-ai/cli` are documented here.
|
|
4
4
|
This project adheres to [Semantic Versioning](https://semver.org).
|
|
5
5
|
|
|
6
|
+
## 0.6.6 — 2026-07-03
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- **Bundled blueprint library updated to `@vectros-ai/blueprints` 0.6.4.** The
|
|
11
|
+
`agentic-sdlc` blueprint's record schemas (`control` / `convention` / `gotcha` /
|
|
12
|
+
`term`) now carry a `sourceRef` provenance field — the sync-back index that lets a
|
|
13
|
+
knowledge base stay in sync with the repo it mirrors — and its bundled guide and
|
|
14
|
+
agent prompt document that sync pattern. No CLI command or flag changes.
|
|
15
|
+
- Updated the bundled `@vectros-ai/sdk` to **0.32.0**, keeping the CLI aligned with
|
|
16
|
+
the current Vectros API. Documents gained a lifecycle `status` (archive/restore
|
|
17
|
+
without deleting) and their processing state moved to `indexStatus`; no CLI
|
|
18
|
+
command or flag changes.
|
|
19
|
+
|
|
20
|
+
## 0.6.5 — 2026-07-01
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **Find your own user id without hunting for it.** `vectros whoami` now shows
|
|
25
|
+
**`your user id`** (`usr_<...>`), and `vectros access grant`/`revoke`/`list`/`get`
|
|
26
|
+
accept **`--principal me`** as an alias for your own user — so joining yourself to
|
|
27
|
+
a context is a one-liner with no id lookup:
|
|
28
|
+
`vectros access grant --principal me --context <ctx> --role <role>`.
|
|
29
|
+
Resolution is client-side (your login's email matched against the tenant's users);
|
|
30
|
+
if it can't be resolved, `whoami` shows `—` and `--principal me` prints how to pass
|
|
31
|
+
the id explicitly (`vectros identity list --type user`).
|
|
32
|
+
|
|
6
33
|
## 0.6.4 — 2026-06-29
|
|
7
34
|
|
|
8
35
|
### Fixed
|
package/dist/cli.js
CHANGED
|
@@ -504,10 +504,18 @@ var DocumentRequest;
|
|
|
504
504
|
Text: "TEXT",
|
|
505
505
|
None: "NONE"
|
|
506
506
|
};
|
|
507
|
+
DocumentRequest2.Status = {
|
|
508
|
+
Active: "ACTIVE",
|
|
509
|
+
Archived: "ARCHIVED"
|
|
510
|
+
};
|
|
507
511
|
})(DocumentRequest || (DocumentRequest = {}));
|
|
508
512
|
var DocumentResponse;
|
|
509
513
|
((DocumentResponse2) => {
|
|
510
514
|
DocumentResponse2.Status = {
|
|
515
|
+
Active: "ACTIVE",
|
|
516
|
+
Archived: "ARCHIVED"
|
|
517
|
+
};
|
|
518
|
+
DocumentResponse2.IndexStatus = {
|
|
511
519
|
PendingUpload: "PENDING_UPLOAD",
|
|
512
520
|
Uploaded: "UPLOADED",
|
|
513
521
|
Extracting: "EXTRACTING",
|
|
@@ -5278,7 +5286,7 @@ var DocumentsClient = class {
|
|
|
5278
5286
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/versions");
|
|
5279
5287
|
}
|
|
5280
5288
|
/**
|
|
5281
|
-
* 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 to the SAME existing document/object (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
|
|
5289
|
+
* 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 to the SAME existing document/object (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).
|
|
5282
5290
|
*
|
|
5283
5291
|
* @param {Vectros.FileUploadRequest} request
|
|
5284
5292
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -9671,9 +9679,22 @@ var codingAgentMemory = {
|
|
|
9671
9679
|
]
|
|
9672
9680
|
};
|
|
9673
9681
|
var coding_agent_memory_default = codingAgentMemory;
|
|
9682
|
+
var DATA_PLANE_ACTIONS = [
|
|
9683
|
+
"records:r",
|
|
9684
|
+
"records:c",
|
|
9685
|
+
"records:u",
|
|
9686
|
+
"search:r",
|
|
9687
|
+
"schemas:r",
|
|
9688
|
+
"inference:r",
|
|
9689
|
+
"documents:r",
|
|
9690
|
+
"documents:c",
|
|
9691
|
+
"folders:r",
|
|
9692
|
+
"folders:c"
|
|
9693
|
+
];
|
|
9694
|
+
var EDITOR_ACTIONS = [...DATA_PLANE_ACTIONS, "records:d", "documents:d", "folders:d"];
|
|
9674
9695
|
var agenticSdlc = {
|
|
9675
9696
|
name: "agentic-sdlc",
|
|
9676
|
-
version: "1.
|
|
9697
|
+
version: "1.2.0",
|
|
9677
9698
|
description: "A whole-SDLC system of record for an AI development team \u2014 decisions, designs, references, runbooks, post-mortems (as documents) plus controls, conventions, gotchas, and a glossary (as records), cross-linked and recalled by meaning.",
|
|
9678
9699
|
contextId: "agentic-sdlc",
|
|
9679
9700
|
contextName: "Agentic SDLC Knowledge Base",
|
|
@@ -10108,6 +10129,19 @@ var agenticSdlc = {
|
|
|
10108
10129
|
fieldType: "date",
|
|
10109
10130
|
description: "ISO-8601 last-reviewed date. Range-queryable.",
|
|
10110
10131
|
renderHints: { label: "Reviewed on", widget: "date", order: 12 }
|
|
10132
|
+
},
|
|
10133
|
+
{
|
|
10134
|
+
// Provenance for sync: the source file this record was distilled from. A
|
|
10135
|
+
// record can't carry an in-file marker the way a document can (many records
|
|
10136
|
+
// come from one file), so it names its source instead — a change to that file
|
|
10137
|
+
// finds (equality lookup) and re-extracts exactly its records. Equality, not
|
|
10138
|
+
// range: file-level is the sync unit (re-extraction reprocesses the whole file),
|
|
10139
|
+
// and the schema keeps its single range lookup for the date row.
|
|
10140
|
+
fieldId: "sourceRef",
|
|
10141
|
+
fieldType: "string",
|
|
10142
|
+
filterable: true,
|
|
10143
|
+
description: "The source file (repo path) this record was extracted from \u2014 its provenance; a change to that file re-extracts its records. The specific section is encoded in the record externalId.",
|
|
10144
|
+
renderHints: { label: "Source ref", widget: "text", order: 13 }
|
|
10111
10145
|
}
|
|
10112
10146
|
],
|
|
10113
10147
|
lookupFields: [
|
|
@@ -10117,7 +10151,8 @@ var agenticSdlc = {
|
|
|
10117
10151
|
"area",
|
|
10118
10152
|
"verifiedBy",
|
|
10119
10153
|
"relatedDecision",
|
|
10120
|
-
{ fieldName: "reviewedOn", rangeEnabled: true }
|
|
10154
|
+
{ fieldName: "reviewedOn", rangeEnabled: true },
|
|
10155
|
+
"sourceRef"
|
|
10121
10156
|
]
|
|
10122
10157
|
},
|
|
10123
10158
|
{
|
|
@@ -10194,9 +10229,23 @@ var agenticSdlc = {
|
|
|
10194
10229
|
fieldType: "date",
|
|
10195
10230
|
description: "ISO-8601 \u2014 when last revised. Range-queryable.",
|
|
10196
10231
|
renderHints: { label: "Updated on", widget: "date", order: 9 }
|
|
10232
|
+
},
|
|
10233
|
+
{
|
|
10234
|
+
// Provenance for sync — see the note on `control.sourceRef`.
|
|
10235
|
+
fieldId: "sourceRef",
|
|
10236
|
+
fieldType: "string",
|
|
10237
|
+
filterable: true,
|
|
10238
|
+
description: "The source file (repo path) this record was extracted from \u2014 its provenance; a change to that file re-extracts its records. The specific section is encoded in the record externalId.",
|
|
10239
|
+
renderHints: { label: "Source ref", widget: "text", order: 10 }
|
|
10197
10240
|
}
|
|
10198
10241
|
],
|
|
10199
|
-
lookupFields: [
|
|
10242
|
+
lookupFields: [
|
|
10243
|
+
"area",
|
|
10244
|
+
"status",
|
|
10245
|
+
"establishedBy",
|
|
10246
|
+
{ fieldName: "updatedOn", rangeEnabled: true },
|
|
10247
|
+
"sourceRef"
|
|
10248
|
+
]
|
|
10200
10249
|
},
|
|
10201
10250
|
{
|
|
10202
10251
|
// A GOTCHA / sharp edge: a symptom, its cause, and the fix. A tight typed
|
|
@@ -10250,9 +10299,22 @@ var agenticSdlc = {
|
|
|
10250
10299
|
fieldType: "date",
|
|
10251
10300
|
description: "ISO-8601 \u2014 when first hit. Range-queryable.",
|
|
10252
10301
|
renderHints: { label: "Discovered on", widget: "date", order: 7 }
|
|
10302
|
+
},
|
|
10303
|
+
{
|
|
10304
|
+
// Provenance for sync — see the note on `control.sourceRef`.
|
|
10305
|
+
fieldId: "sourceRef",
|
|
10306
|
+
fieldType: "string",
|
|
10307
|
+
filterable: true,
|
|
10308
|
+
description: "The source file (repo path) this record was extracted from \u2014 its provenance; a change to that file re-extracts its records. The specific section is encoded in the record externalId.",
|
|
10309
|
+
renderHints: { label: "Source ref", widget: "text", order: 8 }
|
|
10253
10310
|
}
|
|
10254
10311
|
],
|
|
10255
|
-
lookupFields: [
|
|
10312
|
+
lookupFields: [
|
|
10313
|
+
"area",
|
|
10314
|
+
"status",
|
|
10315
|
+
{ fieldName: "discoveredOn", rangeEnabled: true },
|
|
10316
|
+
"sourceRef"
|
|
10317
|
+
]
|
|
10256
10318
|
},
|
|
10257
10319
|
{
|
|
10258
10320
|
// A glossary TERM — a definition keyed by the term itself. Structure-dominant:
|
|
@@ -10312,6 +10374,14 @@ var agenticSdlc = {
|
|
|
10312
10374
|
fieldType: "date",
|
|
10313
10375
|
description: "ISO-8601 \u2014 when last revised. Range-queryable.",
|
|
10314
10376
|
renderHints: { label: "Updated on", widget: "date", order: 7 }
|
|
10377
|
+
},
|
|
10378
|
+
{
|
|
10379
|
+
// Provenance for sync — see the note on `control.sourceRef`.
|
|
10380
|
+
fieldId: "sourceRef",
|
|
10381
|
+
fieldType: "string",
|
|
10382
|
+
filterable: true,
|
|
10383
|
+
description: "The source file (repo path) this record was extracted from \u2014 its provenance; a change to that file re-extracts its records. The specific section is encoded in the record externalId.",
|
|
10384
|
+
renderHints: { label: "Source ref", widget: "text", order: 8 }
|
|
10315
10385
|
}
|
|
10316
10386
|
],
|
|
10317
10387
|
// `term` is a UNIQUE equality lookup — exact "define X" + a one-per-term
|
|
@@ -10320,27 +10390,32 @@ var agenticSdlc = {
|
|
|
10320
10390
|
{ fieldName: "term", unique: true },
|
|
10321
10391
|
"area",
|
|
10322
10392
|
"relatedDecision",
|
|
10323
|
-
{ fieldName: "updatedOn", rangeEnabled: true }
|
|
10393
|
+
{ fieldName: "updatedOn", rangeEnabled: true },
|
|
10394
|
+
"sourceRef"
|
|
10324
10395
|
]
|
|
10325
10396
|
}
|
|
10326
10397
|
],
|
|
10327
|
-
// Least-privilege, data-plane only.
|
|
10328
|
-
//
|
|
10329
|
-
//
|
|
10330
|
-
// via a status flip, so the trail of how the team's thinking evolved stays intact.
|
|
10398
|
+
// Least-privilege, data-plane only. The scope of the `ssk_*` key the bootstrap
|
|
10399
|
+
// mints for THIS blueprint's service principal (the MCP/API runtime). See
|
|
10400
|
+
// DATA_PLANE_ACTIONS above for the action set + rationale.
|
|
10331
10401
|
accessProfile: {
|
|
10332
|
-
allowedActions:
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10402
|
+
allowedActions: DATA_PLANE_ACTIONS
|
|
10403
|
+
},
|
|
10404
|
+
// A reusable `editor` role for the HUMAN owner — DISTINCT from `accessProfile`
|
|
10405
|
+
// (which scopes only the service-principal key). `bootstrap` provisions this
|
|
10406
|
+
// role in the context but binds it to no one; the owner joins themselves so the
|
|
10407
|
+
// data-plane app (app.vectros.ai) shows their KB — its switcher lists only
|
|
10408
|
+
// contexts the signed-in user holds an active access profile in, and bootstrap
|
|
10409
|
+
// grants the human none by default. Bind it after bootstrap with:
|
|
10410
|
+
// vectros access grant --principal usr_<your-user-id> --context agentic-sdlc --role editor
|
|
10411
|
+
// (or the admin app's Access > Contexts > agentic-sdlc > Profiles > Create).
|
|
10412
|
+
// The editor gets the full data plane so a human curator can browse, write/correct,
|
|
10413
|
+
// AND hard-delete the KB (EDITOR_ACTIONS = the service key's set + delete). The
|
|
10414
|
+
// service key deliberately lacks delete and archives instead; the trusted human
|
|
10415
|
+
// owner may permanently remove genuine strays. Still no control-plane action, so
|
|
10416
|
+
// the scope gate accepts it as a data-plane-only role.
|
|
10417
|
+
roles: {
|
|
10418
|
+
editor: [{ allowedActions: EDITOR_ACTIONS }]
|
|
10344
10419
|
},
|
|
10345
10420
|
servicePrincipal: {
|
|
10346
10421
|
externalId: "agentic-sdlc",
|
|
@@ -12625,12 +12700,63 @@ function splitCsv(csv) {
|
|
|
12625
12700
|
return csv.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
12626
12701
|
}
|
|
12627
12702
|
|
|
12703
|
+
// src/paginate.ts
|
|
12704
|
+
var PAGE_LIMIT = 100;
|
|
12705
|
+
async function drainPages(fetch2) {
|
|
12706
|
+
const out = [];
|
|
12707
|
+
let startFrom;
|
|
12708
|
+
for (; ; ) {
|
|
12709
|
+
const page = await fetch2(startFrom);
|
|
12710
|
+
if (page.data) out.push(...page.data);
|
|
12711
|
+
const next = page.nextCursor;
|
|
12712
|
+
if (!next) break;
|
|
12713
|
+
startFrom = next;
|
|
12714
|
+
}
|
|
12715
|
+
return out;
|
|
12716
|
+
}
|
|
12717
|
+
|
|
12718
|
+
// src/self-principal.ts
|
|
12719
|
+
var PRINCIPAL_PREFIX_USER = "usr_";
|
|
12720
|
+
function emailFromJwt(token) {
|
|
12721
|
+
const parts = token.split(".");
|
|
12722
|
+
if (parts.length < 2) return void 0;
|
|
12723
|
+
try {
|
|
12724
|
+
const payload = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
12725
|
+
const claims = JSON.parse(payload);
|
|
12726
|
+
return typeof claims.email === "string" && claims.email.trim() ? claims.email.trim() : void 0;
|
|
12727
|
+
} catch {
|
|
12728
|
+
return void 0;
|
|
12729
|
+
}
|
|
12730
|
+
}
|
|
12731
|
+
async function resolveSelfPrincipal(client, cognitoToken) {
|
|
12732
|
+
const email = emailFromJwt(cognitoToken);
|
|
12733
|
+
if (!email) return null;
|
|
12734
|
+
const target = email.toLowerCase();
|
|
12735
|
+
const users = await drainPages(
|
|
12736
|
+
(startFrom) => client.identity.listUsers(
|
|
12737
|
+
startFrom === void 0 ? { limit: PAGE_LIMIT } : { startFrom, limit: PAGE_LIMIT }
|
|
12738
|
+
)
|
|
12739
|
+
);
|
|
12740
|
+
const match = users.find(
|
|
12741
|
+
(u) => u.id && typeof u.email === "string" && u.email.toLowerCase() === target
|
|
12742
|
+
);
|
|
12743
|
+
if (!match?.id) return null;
|
|
12744
|
+
return { principalId: `${PRINCIPAL_PREFIX_USER}${match.id}`, userId: match.id, email };
|
|
12745
|
+
}
|
|
12746
|
+
|
|
12628
12747
|
// src/commands/whoami.ts
|
|
12629
12748
|
async function runWhoami(options, deps = {}) {
|
|
12630
12749
|
return withAuth(options, deps, async (auth) => {
|
|
12631
12750
|
const { identity, baseUrl } = auth;
|
|
12751
|
+
let self2 = null;
|
|
12752
|
+
try {
|
|
12753
|
+
self2 = await resolveSelfPrincipal(auth.client, auth.cognitoToken);
|
|
12754
|
+
} catch {
|
|
12755
|
+
self2 = null;
|
|
12756
|
+
}
|
|
12632
12757
|
if (options.json) {
|
|
12633
|
-
|
|
12758
|
+
const out = self2 ? { ...identity, userId: self2.userId, userPrincipalId: self2.principalId } : identity;
|
|
12759
|
+
process.stdout.write(`${formatJson(out)}
|
|
12634
12760
|
`);
|
|
12635
12761
|
return 0;
|
|
12636
12762
|
}
|
|
@@ -12642,6 +12768,7 @@ async function runWhoami(options, deps = {}) {
|
|
|
12642
12768
|
["principal", identity.principalType],
|
|
12643
12769
|
["principal id", identity.principalKeyId],
|
|
12644
12770
|
["label", identity.principalLabel],
|
|
12771
|
+
["your user id", self2?.principalId],
|
|
12645
12772
|
["allowed actions", identity.allowedActions?.length ? identity.allowedActions.join(", ") : void 0],
|
|
12646
12773
|
["token expires", expiry],
|
|
12647
12774
|
["api", baseUrl]
|
|
@@ -12651,21 +12778,6 @@ async function runWhoami(options, deps = {}) {
|
|
|
12651
12778
|
});
|
|
12652
12779
|
}
|
|
12653
12780
|
|
|
12654
|
-
// src/paginate.ts
|
|
12655
|
-
var PAGE_LIMIT = 100;
|
|
12656
|
-
async function drainPages(fetch2) {
|
|
12657
|
-
const out = [];
|
|
12658
|
-
let startFrom;
|
|
12659
|
-
for (; ; ) {
|
|
12660
|
-
const page = await fetch2(startFrom);
|
|
12661
|
-
if (page.data) out.push(...page.data);
|
|
12662
|
-
const next = page.nextCursor;
|
|
12663
|
-
if (!next) break;
|
|
12664
|
-
startFrom = next;
|
|
12665
|
-
}
|
|
12666
|
-
return out;
|
|
12667
|
-
}
|
|
12668
|
-
|
|
12669
12781
|
// src/commands/key.ts
|
|
12670
12782
|
var DEFAULT_KEY_NAME = "default";
|
|
12671
12783
|
function emitMintedKey(key, format) {
|
|
@@ -13055,6 +13167,17 @@ function assertPrincipalShape(principal) {
|
|
|
13055
13167
|
throw new Error(`--principal must be 'usr_<userId>' or 'key_<keyId>' (got '${principal}').`);
|
|
13056
13168
|
}
|
|
13057
13169
|
}
|
|
13170
|
+
var SELF_PRINCIPAL_ALIAS = "me";
|
|
13171
|
+
async function resolvePrincipalArg(raw, auth) {
|
|
13172
|
+
if (raw !== SELF_PRINCIPAL_ALIAS) return raw;
|
|
13173
|
+
const self2 = await resolveSelfPrincipal(auth.client, auth.cognitoToken);
|
|
13174
|
+
if (!self2) {
|
|
13175
|
+
throw new Error(
|
|
13176
|
+
"Could not resolve your own user id from your login. Pass --principal usr_<id> explicitly (find the id with `vectros identity list --type user`)."
|
|
13177
|
+
);
|
|
13178
|
+
}
|
|
13179
|
+
return self2.principalId;
|
|
13180
|
+
}
|
|
13058
13181
|
function renderProfile(p) {
|
|
13059
13182
|
return formatKeyValues([
|
|
13060
13183
|
["context", p.contextId],
|
|
@@ -13071,21 +13194,24 @@ async function runAccessGrant(opts, deps = {}) {
|
|
|
13071
13194
|
process.stderr.write(" \u2716 Provide exactly one of --role <roleId> or --actions <csv>.\n");
|
|
13072
13195
|
return 2;
|
|
13073
13196
|
}
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13197
|
+
if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
|
|
13198
|
+
try {
|
|
13199
|
+
assertPrincipalShape(opts.principal);
|
|
13200
|
+
} catch (err) {
|
|
13201
|
+
process.stderr.write(` \u2716 ${formatError(err)}
|
|
13078
13202
|
`);
|
|
13079
|
-
|
|
13203
|
+
return 2;
|
|
13204
|
+
}
|
|
13080
13205
|
}
|
|
13081
13206
|
return withAuth(opts, deps, async (auth) => {
|
|
13082
13207
|
const client = auth.client;
|
|
13083
13208
|
try {
|
|
13209
|
+
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
13084
13210
|
const scopes = hasActions ? [{ allowed_actions: splitCsv(opts.actions) }] : void 0;
|
|
13085
13211
|
const profile = await client.auth.createAccessProfile({
|
|
13086
13212
|
contextId: opts.context,
|
|
13087
13213
|
body: {
|
|
13088
|
-
principalId:
|
|
13214
|
+
principalId: principal,
|
|
13089
13215
|
roleId: opts.role,
|
|
13090
13216
|
scopes
|
|
13091
13217
|
}
|
|
@@ -13097,7 +13223,7 @@ async function runAccessGrant(opts, deps = {}) {
|
|
|
13097
13223
|
}
|
|
13098
13224
|
process.stdout.write(
|
|
13099
13225
|
`
|
|
13100
|
-
\u2714 Granted ${
|
|
13226
|
+
\u2714 Granted ${principal} in '${opts.context}'` + (hasRole ? ` via role '${opts.role}'` : ` with ${splitCsv(opts.actions).length} action(s)`) + `
|
|
13101
13227
|
${renderProfile(profile)}
|
|
13102
13228
|
`
|
|
13103
13229
|
);
|
|
@@ -13110,18 +13236,21 @@ ${renderProfile(profile)}
|
|
|
13110
13236
|
});
|
|
13111
13237
|
}
|
|
13112
13238
|
async function runAccessRevoke(opts, deps = {}) {
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
13239
|
+
if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
|
|
13240
|
+
try {
|
|
13241
|
+
assertPrincipalShape(opts.principal);
|
|
13242
|
+
} catch (err) {
|
|
13243
|
+
process.stderr.write(` \u2716 ${formatError(err)}
|
|
13117
13244
|
`);
|
|
13118
|
-
|
|
13245
|
+
return 2;
|
|
13246
|
+
}
|
|
13119
13247
|
}
|
|
13120
13248
|
return withAuth(opts, deps, async (auth) => {
|
|
13121
13249
|
const client = auth.client;
|
|
13122
13250
|
try {
|
|
13123
|
-
|
|
13124
|
-
|
|
13251
|
+
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
13252
|
+
await client.auth.deleteAccessProfile({ contextId: opts.context, principalId: principal });
|
|
13253
|
+
process.stdout.write(` \u2714 Revoked ${principal} from '${opts.context}'
|
|
13125
13254
|
`);
|
|
13126
13255
|
return 0;
|
|
13127
13256
|
} catch (err) {
|
|
@@ -13136,7 +13265,7 @@ async function runAccessList(opts, deps = {}) {
|
|
|
13136
13265
|
process.stderr.write(" \u2716 Provide exactly one of --context <c> or --principal <p>.\n");
|
|
13137
13266
|
return 2;
|
|
13138
13267
|
}
|
|
13139
|
-
if (opts.principal) {
|
|
13268
|
+
if (opts.principal && opts.principal !== SELF_PRINCIPAL_ALIAS) {
|
|
13140
13269
|
try {
|
|
13141
13270
|
assertPrincipalShape(opts.principal);
|
|
13142
13271
|
} catch (err) {
|
|
@@ -13148,10 +13277,11 @@ async function runAccessList(opts, deps = {}) {
|
|
|
13148
13277
|
return withAuth(opts, deps, async (auth) => {
|
|
13149
13278
|
const client = auth.client;
|
|
13150
13279
|
try {
|
|
13280
|
+
const principal = opts.principal ? await resolvePrincipalArg(opts.principal, auth) : void 0;
|
|
13151
13281
|
const rows = opts.context ? await drainPages(
|
|
13152
13282
|
(startFrom) => client.auth.listAccessProfiles({ contextId: opts.context, startFrom, limit: PAGE_LIMIT })
|
|
13153
13283
|
) : await drainPages(
|
|
13154
|
-
(startFrom) => client.auth.listProfilesForPrincipal({ principalId:
|
|
13284
|
+
(startFrom) => client.auth.listProfilesForPrincipal({ principalId: principal, startFrom, limit: PAGE_LIMIT })
|
|
13155
13285
|
);
|
|
13156
13286
|
if (opts.json) {
|
|
13157
13287
|
process.stdout.write(`${formatJson(rows)}
|
|
@@ -13175,17 +13305,20 @@ async function runAccessList(opts, deps = {}) {
|
|
|
13175
13305
|
});
|
|
13176
13306
|
}
|
|
13177
13307
|
async function runAccessGet(opts, deps = {}) {
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13308
|
+
if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
|
|
13309
|
+
try {
|
|
13310
|
+
assertPrincipalShape(opts.principal);
|
|
13311
|
+
} catch (err) {
|
|
13312
|
+
process.stderr.write(` \u2716 ${formatError(err)}
|
|
13182
13313
|
`);
|
|
13183
|
-
|
|
13314
|
+
return 2;
|
|
13315
|
+
}
|
|
13184
13316
|
}
|
|
13185
13317
|
return withAuth(opts, deps, async (auth) => {
|
|
13186
13318
|
const client = auth.client;
|
|
13187
13319
|
try {
|
|
13188
|
-
const
|
|
13320
|
+
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
13321
|
+
const p = await client.auth.getAccessProfile({ contextId: opts.context, principalId: principal });
|
|
13189
13322
|
if (opts.json) {
|
|
13190
13323
|
process.stdout.write(`${formatJson(p)}
|
|
13191
13324
|
`);
|
|
@@ -13961,9 +14094,9 @@ async function runBlueprintTest(fileOrName, opts, deps = {}) {
|
|
|
13961
14094
|
|
|
13962
14095
|
// src/build-info.ts
|
|
13963
14096
|
var BUILD_INFO = {
|
|
13964
|
-
cli: true ? "0.6.
|
|
13965
|
-
sdk: true ? "0.
|
|
13966
|
-
blueprints: true ? "0.6.
|
|
14097
|
+
cli: true ? "0.6.6" : "dev",
|
|
14098
|
+
sdk: true ? "0.32.0" : "dev",
|
|
14099
|
+
blueprints: true ? "0.6.4" : "dev"
|
|
13967
14100
|
};
|
|
13968
14101
|
function formatBuildInfo() {
|
|
13969
14102
|
return `vectros ${BUILD_INFO.cli} (sdk ${BUILD_INFO.sdk}, blueprints ${BUILD_INFO.blueprints})`;
|
|
@@ -14082,7 +14215,7 @@ function registerRoleCommands(program, deps) {
|
|
|
14082
14215
|
}
|
|
14083
14216
|
function registerAccessCommands(program, deps) {
|
|
14084
14217
|
const access2 = program.command("access").description("Manage access bindings: principal \u2194 context \u2194 role/scopes.");
|
|
14085
|
-
const grant = access2.command("grant").description("Bind a principal to a context via a role or inline actions.").requiredOption("--principal <p>", "principal: 'usr_<userId>'
|
|
14218
|
+
const grant = access2.command("grant").description("Bind a principal to a context via a role or inline actions.").requiredOption("--principal <p>", "principal: 'usr_<userId>', 'key_<keyId>', or 'me' (your own user)").requiredOption("--context <c>", "app context").option("--role <roleId>", "reference a Role (XOR --actions)").option("--actions <csv>", "inline single-clause actions, comma-separated (XOR --role)");
|
|
14086
14219
|
addCommonAuthOptions(grant).action(
|
|
14087
14220
|
async (flags) => {
|
|
14088
14221
|
process.exit(
|
|
@@ -14096,15 +14229,15 @@ function registerAccessCommands(program, deps) {
|
|
|
14096
14229
|
);
|
|
14097
14230
|
}
|
|
14098
14231
|
);
|
|
14099
|
-
const revoke = access2.command("revoke").description("Remove a principal\u2019s binding in a context.").requiredOption("--principal <p>", "principal: 'usr_<userId>'
|
|
14232
|
+
const revoke = access2.command("revoke").description("Remove a principal\u2019s binding in a context.").requiredOption("--principal <p>", "principal: 'usr_<userId>', 'key_<keyId>', or 'me' (your own user)").requiredOption("--context <c>", "app context");
|
|
14100
14233
|
addAuthOptions(revoke).action(async (flags) => {
|
|
14101
14234
|
process.exit(await runAccessRevoke({ ...toCommonOptions(flags), principal: flags.principal, context: flags.context }, deps));
|
|
14102
14235
|
});
|
|
14103
|
-
const list = access2.command("list").description("List a context\u2019s members (--context) or a principal\u2019s contexts (--principal).").option("--context <c>", "list members of this context").option("--principal <p>", "list contexts this principal is bound to");
|
|
14236
|
+
const list = access2.command("list").description("List a context\u2019s members (--context) or a principal\u2019s contexts (--principal).").option("--context <c>", "list members of this context").option("--principal <p>", "list contexts this principal is bound to ('me' = your own user)");
|
|
14104
14237
|
addCommonAuthOptions(list).action(async (flags) => {
|
|
14105
14238
|
process.exit(await runAccessList({ ...toCommonOptions(flags), context: flags.context, principal: flags.principal }, deps));
|
|
14106
14239
|
});
|
|
14107
|
-
const get = access2.command("get").description("Show one principal\u2019s binding in a context.").requiredOption("--principal <p>", "principal: 'usr_<userId>'
|
|
14240
|
+
const get = access2.command("get").description("Show one principal\u2019s binding in a context.").requiredOption("--principal <p>", "principal: 'usr_<userId>', 'key_<keyId>', or 'me' (your own user)").requiredOption("--context <c>", "app context");
|
|
14108
14241
|
addCommonAuthOptions(get).action(async (flags) => {
|
|
14109
14242
|
process.exit(await runAccessGet({ ...toCommonOptions(flags), principal: flags.principal, context: flags.context }, deps));
|
|
14110
14243
|
});
|