@vectros-ai/cli 0.6.4 → 0.6.5
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 +13 -0
- package/dist/cli.js +132 -60
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +132 -60
- package/dist/cli.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
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.5 — 2026-07-01
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Find your own user id without hunting for it.** `vectros whoami` now shows
|
|
11
|
+
**`your user id`** (`usr_<...>`), and `vectros access grant`/`revoke`/`list`/`get`
|
|
12
|
+
accept **`--principal me`** as an alias for your own user — so joining yourself to
|
|
13
|
+
a context is a one-liner with no id lookup:
|
|
14
|
+
`vectros access grant --principal me --context <ctx> --role <role>`.
|
|
15
|
+
Resolution is client-side (your login's email matched against the tenant's users);
|
|
16
|
+
if it can't be resolved, `whoami` shows `—` and `--principal me` prints how to pass
|
|
17
|
+
the id explicitly (`vectros identity list --type user`).
|
|
18
|
+
|
|
6
19
|
## 0.6.4 — 2026-06-29
|
|
7
20
|
|
|
8
21
|
### Fixed
|
package/dist/cli.js
CHANGED
|
@@ -9671,6 +9671,18 @@ var codingAgentMemory = {
|
|
|
9671
9671
|
]
|
|
9672
9672
|
};
|
|
9673
9673
|
var coding_agent_memory_default = codingAgentMemory;
|
|
9674
|
+
var DATA_PLANE_ACTIONS = [
|
|
9675
|
+
"records:r",
|
|
9676
|
+
"records:c",
|
|
9677
|
+
"records:u",
|
|
9678
|
+
"search:r",
|
|
9679
|
+
"schemas:r",
|
|
9680
|
+
"inference:r",
|
|
9681
|
+
"documents:r",
|
|
9682
|
+
"documents:c",
|
|
9683
|
+
"folders:r",
|
|
9684
|
+
"folders:c"
|
|
9685
|
+
];
|
|
9674
9686
|
var agenticSdlc = {
|
|
9675
9687
|
name: "agentic-sdlc",
|
|
9676
9688
|
version: "1.0.0",
|
|
@@ -10324,23 +10336,25 @@ var agenticSdlc = {
|
|
|
10324
10336
|
]
|
|
10325
10337
|
}
|
|
10326
10338
|
],
|
|
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.
|
|
10339
|
+
// Least-privilege, data-plane only. The scope of the `ssk_*` key the bootstrap
|
|
10340
|
+
// mints for THIS blueprint's service principal (the MCP/API runtime). See
|
|
10341
|
+
// DATA_PLANE_ACTIONS above for the action set + rationale.
|
|
10331
10342
|
accessProfile: {
|
|
10332
|
-
allowedActions:
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10343
|
+
allowedActions: DATA_PLANE_ACTIONS
|
|
10344
|
+
},
|
|
10345
|
+
// A reusable `editor` role for the HUMAN owner — DISTINCT from `accessProfile`
|
|
10346
|
+
// (which scopes only the service-principal key). `bootstrap` provisions this
|
|
10347
|
+
// role in the context but binds it to no one; the owner joins themselves so the
|
|
10348
|
+
// data-plane app (app.vectros.ai) shows their KB — its switcher lists only
|
|
10349
|
+
// contexts the signed-in user holds an active access profile in, and bootstrap
|
|
10350
|
+
// grants the human none by default. Bind it after bootstrap with:
|
|
10351
|
+
// vectros access grant --principal usr_<your-user-id> --context agentic-sdlc --role editor
|
|
10352
|
+
// (or the admin app's Access > Contexts > agentic-sdlc > Profiles > Create).
|
|
10353
|
+
// Editor PARITY with the service key (same DATA_PLANE_ACTIONS) so a human
|
|
10354
|
+
// curator can browse AND write/correct the KB; still no :d and no control-plane
|
|
10355
|
+
// action, so the scope gate accepts it exactly like the accessProfile.
|
|
10356
|
+
roles: {
|
|
10357
|
+
editor: [{ allowedActions: DATA_PLANE_ACTIONS }]
|
|
10344
10358
|
},
|
|
10345
10359
|
servicePrincipal: {
|
|
10346
10360
|
externalId: "agentic-sdlc",
|
|
@@ -12625,12 +12639,63 @@ function splitCsv(csv) {
|
|
|
12625
12639
|
return csv.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
12626
12640
|
}
|
|
12627
12641
|
|
|
12642
|
+
// src/paginate.ts
|
|
12643
|
+
var PAGE_LIMIT = 100;
|
|
12644
|
+
async function drainPages(fetch2) {
|
|
12645
|
+
const out = [];
|
|
12646
|
+
let startFrom;
|
|
12647
|
+
for (; ; ) {
|
|
12648
|
+
const page = await fetch2(startFrom);
|
|
12649
|
+
if (page.data) out.push(...page.data);
|
|
12650
|
+
const next = page.nextCursor;
|
|
12651
|
+
if (!next) break;
|
|
12652
|
+
startFrom = next;
|
|
12653
|
+
}
|
|
12654
|
+
return out;
|
|
12655
|
+
}
|
|
12656
|
+
|
|
12657
|
+
// src/self-principal.ts
|
|
12658
|
+
var PRINCIPAL_PREFIX_USER = "usr_";
|
|
12659
|
+
function emailFromJwt(token) {
|
|
12660
|
+
const parts = token.split(".");
|
|
12661
|
+
if (parts.length < 2) return void 0;
|
|
12662
|
+
try {
|
|
12663
|
+
const payload = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
12664
|
+
const claims = JSON.parse(payload);
|
|
12665
|
+
return typeof claims.email === "string" && claims.email.trim() ? claims.email.trim() : void 0;
|
|
12666
|
+
} catch {
|
|
12667
|
+
return void 0;
|
|
12668
|
+
}
|
|
12669
|
+
}
|
|
12670
|
+
async function resolveSelfPrincipal(client, cognitoToken) {
|
|
12671
|
+
const email = emailFromJwt(cognitoToken);
|
|
12672
|
+
if (!email) return null;
|
|
12673
|
+
const target = email.toLowerCase();
|
|
12674
|
+
const users = await drainPages(
|
|
12675
|
+
(startFrom) => client.identity.listUsers(
|
|
12676
|
+
startFrom === void 0 ? { limit: PAGE_LIMIT } : { startFrom, limit: PAGE_LIMIT }
|
|
12677
|
+
)
|
|
12678
|
+
);
|
|
12679
|
+
const match = users.find(
|
|
12680
|
+
(u) => u.id && typeof u.email === "string" && u.email.toLowerCase() === target
|
|
12681
|
+
);
|
|
12682
|
+
if (!match?.id) return null;
|
|
12683
|
+
return { principalId: `${PRINCIPAL_PREFIX_USER}${match.id}`, userId: match.id, email };
|
|
12684
|
+
}
|
|
12685
|
+
|
|
12628
12686
|
// src/commands/whoami.ts
|
|
12629
12687
|
async function runWhoami(options, deps = {}) {
|
|
12630
12688
|
return withAuth(options, deps, async (auth) => {
|
|
12631
12689
|
const { identity, baseUrl } = auth;
|
|
12690
|
+
let self2 = null;
|
|
12691
|
+
try {
|
|
12692
|
+
self2 = await resolveSelfPrincipal(auth.client, auth.cognitoToken);
|
|
12693
|
+
} catch {
|
|
12694
|
+
self2 = null;
|
|
12695
|
+
}
|
|
12632
12696
|
if (options.json) {
|
|
12633
|
-
|
|
12697
|
+
const out = self2 ? { ...identity, userId: self2.userId, userPrincipalId: self2.principalId } : identity;
|
|
12698
|
+
process.stdout.write(`${formatJson(out)}
|
|
12634
12699
|
`);
|
|
12635
12700
|
return 0;
|
|
12636
12701
|
}
|
|
@@ -12642,6 +12707,7 @@ async function runWhoami(options, deps = {}) {
|
|
|
12642
12707
|
["principal", identity.principalType],
|
|
12643
12708
|
["principal id", identity.principalKeyId],
|
|
12644
12709
|
["label", identity.principalLabel],
|
|
12710
|
+
["your user id", self2?.principalId],
|
|
12645
12711
|
["allowed actions", identity.allowedActions?.length ? identity.allowedActions.join(", ") : void 0],
|
|
12646
12712
|
["token expires", expiry],
|
|
12647
12713
|
["api", baseUrl]
|
|
@@ -12651,21 +12717,6 @@ async function runWhoami(options, deps = {}) {
|
|
|
12651
12717
|
});
|
|
12652
12718
|
}
|
|
12653
12719
|
|
|
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
12720
|
// src/commands/key.ts
|
|
12670
12721
|
var DEFAULT_KEY_NAME = "default";
|
|
12671
12722
|
function emitMintedKey(key, format) {
|
|
@@ -13055,6 +13106,17 @@ function assertPrincipalShape(principal) {
|
|
|
13055
13106
|
throw new Error(`--principal must be 'usr_<userId>' or 'key_<keyId>' (got '${principal}').`);
|
|
13056
13107
|
}
|
|
13057
13108
|
}
|
|
13109
|
+
var SELF_PRINCIPAL_ALIAS = "me";
|
|
13110
|
+
async function resolvePrincipalArg(raw, auth) {
|
|
13111
|
+
if (raw !== SELF_PRINCIPAL_ALIAS) return raw;
|
|
13112
|
+
const self2 = await resolveSelfPrincipal(auth.client, auth.cognitoToken);
|
|
13113
|
+
if (!self2) {
|
|
13114
|
+
throw new Error(
|
|
13115
|
+
"Could not resolve your own user id from your login. Pass --principal usr_<id> explicitly (find the id with `vectros identity list --type user`)."
|
|
13116
|
+
);
|
|
13117
|
+
}
|
|
13118
|
+
return self2.principalId;
|
|
13119
|
+
}
|
|
13058
13120
|
function renderProfile(p) {
|
|
13059
13121
|
return formatKeyValues([
|
|
13060
13122
|
["context", p.contextId],
|
|
@@ -13071,21 +13133,24 @@ async function runAccessGrant(opts, deps = {}) {
|
|
|
13071
13133
|
process.stderr.write(" \u2716 Provide exactly one of --role <roleId> or --actions <csv>.\n");
|
|
13072
13134
|
return 2;
|
|
13073
13135
|
}
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13136
|
+
if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
|
|
13137
|
+
try {
|
|
13138
|
+
assertPrincipalShape(opts.principal);
|
|
13139
|
+
} catch (err) {
|
|
13140
|
+
process.stderr.write(` \u2716 ${formatError(err)}
|
|
13078
13141
|
`);
|
|
13079
|
-
|
|
13142
|
+
return 2;
|
|
13143
|
+
}
|
|
13080
13144
|
}
|
|
13081
13145
|
return withAuth(opts, deps, async (auth) => {
|
|
13082
13146
|
const client = auth.client;
|
|
13083
13147
|
try {
|
|
13148
|
+
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
13084
13149
|
const scopes = hasActions ? [{ allowed_actions: splitCsv(opts.actions) }] : void 0;
|
|
13085
13150
|
const profile = await client.auth.createAccessProfile({
|
|
13086
13151
|
contextId: opts.context,
|
|
13087
13152
|
body: {
|
|
13088
|
-
principalId:
|
|
13153
|
+
principalId: principal,
|
|
13089
13154
|
roleId: opts.role,
|
|
13090
13155
|
scopes
|
|
13091
13156
|
}
|
|
@@ -13097,7 +13162,7 @@ async function runAccessGrant(opts, deps = {}) {
|
|
|
13097
13162
|
}
|
|
13098
13163
|
process.stdout.write(
|
|
13099
13164
|
`
|
|
13100
|
-
\u2714 Granted ${
|
|
13165
|
+
\u2714 Granted ${principal} in '${opts.context}'` + (hasRole ? ` via role '${opts.role}'` : ` with ${splitCsv(opts.actions).length} action(s)`) + `
|
|
13101
13166
|
${renderProfile(profile)}
|
|
13102
13167
|
`
|
|
13103
13168
|
);
|
|
@@ -13110,18 +13175,21 @@ ${renderProfile(profile)}
|
|
|
13110
13175
|
});
|
|
13111
13176
|
}
|
|
13112
13177
|
async function runAccessRevoke(opts, deps = {}) {
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
13178
|
+
if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
|
|
13179
|
+
try {
|
|
13180
|
+
assertPrincipalShape(opts.principal);
|
|
13181
|
+
} catch (err) {
|
|
13182
|
+
process.stderr.write(` \u2716 ${formatError(err)}
|
|
13117
13183
|
`);
|
|
13118
|
-
|
|
13184
|
+
return 2;
|
|
13185
|
+
}
|
|
13119
13186
|
}
|
|
13120
13187
|
return withAuth(opts, deps, async (auth) => {
|
|
13121
13188
|
const client = auth.client;
|
|
13122
13189
|
try {
|
|
13123
|
-
|
|
13124
|
-
|
|
13190
|
+
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
13191
|
+
await client.auth.deleteAccessProfile({ contextId: opts.context, principalId: principal });
|
|
13192
|
+
process.stdout.write(` \u2714 Revoked ${principal} from '${opts.context}'
|
|
13125
13193
|
`);
|
|
13126
13194
|
return 0;
|
|
13127
13195
|
} catch (err) {
|
|
@@ -13136,7 +13204,7 @@ async function runAccessList(opts, deps = {}) {
|
|
|
13136
13204
|
process.stderr.write(" \u2716 Provide exactly one of --context <c> or --principal <p>.\n");
|
|
13137
13205
|
return 2;
|
|
13138
13206
|
}
|
|
13139
|
-
if (opts.principal) {
|
|
13207
|
+
if (opts.principal && opts.principal !== SELF_PRINCIPAL_ALIAS) {
|
|
13140
13208
|
try {
|
|
13141
13209
|
assertPrincipalShape(opts.principal);
|
|
13142
13210
|
} catch (err) {
|
|
@@ -13148,10 +13216,11 @@ async function runAccessList(opts, deps = {}) {
|
|
|
13148
13216
|
return withAuth(opts, deps, async (auth) => {
|
|
13149
13217
|
const client = auth.client;
|
|
13150
13218
|
try {
|
|
13219
|
+
const principal = opts.principal ? await resolvePrincipalArg(opts.principal, auth) : void 0;
|
|
13151
13220
|
const rows = opts.context ? await drainPages(
|
|
13152
13221
|
(startFrom) => client.auth.listAccessProfiles({ contextId: opts.context, startFrom, limit: PAGE_LIMIT })
|
|
13153
13222
|
) : await drainPages(
|
|
13154
|
-
(startFrom) => client.auth.listProfilesForPrincipal({ principalId:
|
|
13223
|
+
(startFrom) => client.auth.listProfilesForPrincipal({ principalId: principal, startFrom, limit: PAGE_LIMIT })
|
|
13155
13224
|
);
|
|
13156
13225
|
if (opts.json) {
|
|
13157
13226
|
process.stdout.write(`${formatJson(rows)}
|
|
@@ -13175,17 +13244,20 @@ async function runAccessList(opts, deps = {}) {
|
|
|
13175
13244
|
});
|
|
13176
13245
|
}
|
|
13177
13246
|
async function runAccessGet(opts, deps = {}) {
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13247
|
+
if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
|
|
13248
|
+
try {
|
|
13249
|
+
assertPrincipalShape(opts.principal);
|
|
13250
|
+
} catch (err) {
|
|
13251
|
+
process.stderr.write(` \u2716 ${formatError(err)}
|
|
13182
13252
|
`);
|
|
13183
|
-
|
|
13253
|
+
return 2;
|
|
13254
|
+
}
|
|
13184
13255
|
}
|
|
13185
13256
|
return withAuth(opts, deps, async (auth) => {
|
|
13186
13257
|
const client = auth.client;
|
|
13187
13258
|
try {
|
|
13188
|
-
const
|
|
13259
|
+
const principal = await resolvePrincipalArg(opts.principal, auth);
|
|
13260
|
+
const p = await client.auth.getAccessProfile({ contextId: opts.context, principalId: principal });
|
|
13189
13261
|
if (opts.json) {
|
|
13190
13262
|
process.stdout.write(`${formatJson(p)}
|
|
13191
13263
|
`);
|
|
@@ -13961,9 +14033,9 @@ async function runBlueprintTest(fileOrName, opts, deps = {}) {
|
|
|
13961
14033
|
|
|
13962
14034
|
// src/build-info.ts
|
|
13963
14035
|
var BUILD_INFO = {
|
|
13964
|
-
cli: true ? "0.6.
|
|
14036
|
+
cli: true ? "0.6.5" : "dev",
|
|
13965
14037
|
sdk: true ? "0.31.0" : "dev",
|
|
13966
|
-
blueprints: true ? "0.6.
|
|
14038
|
+
blueprints: true ? "0.6.3" : "dev"
|
|
13967
14039
|
};
|
|
13968
14040
|
function formatBuildInfo() {
|
|
13969
14041
|
return `vectros ${BUILD_INFO.cli} (sdk ${BUILD_INFO.sdk}, blueprints ${BUILD_INFO.blueprints})`;
|
|
@@ -14082,7 +14154,7 @@ function registerRoleCommands(program, deps) {
|
|
|
14082
14154
|
}
|
|
14083
14155
|
function registerAccessCommands(program, deps) {
|
|
14084
14156
|
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>'
|
|
14157
|
+
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
14158
|
addCommonAuthOptions(grant).action(
|
|
14087
14159
|
async (flags) => {
|
|
14088
14160
|
process.exit(
|
|
@@ -14096,15 +14168,15 @@ function registerAccessCommands(program, deps) {
|
|
|
14096
14168
|
);
|
|
14097
14169
|
}
|
|
14098
14170
|
);
|
|
14099
|
-
const revoke = access2.command("revoke").description("Remove a principal\u2019s binding in a context.").requiredOption("--principal <p>", "principal: 'usr_<userId>'
|
|
14171
|
+
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
14172
|
addAuthOptions(revoke).action(async (flags) => {
|
|
14101
14173
|
process.exit(await runAccessRevoke({ ...toCommonOptions(flags), principal: flags.principal, context: flags.context }, deps));
|
|
14102
14174
|
});
|
|
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");
|
|
14175
|
+
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
14176
|
addCommonAuthOptions(list).action(async (flags) => {
|
|
14105
14177
|
process.exit(await runAccessList({ ...toCommonOptions(flags), context: flags.context, principal: flags.principal }, deps));
|
|
14106
14178
|
});
|
|
14107
|
-
const get = access2.command("get").description("Show one principal\u2019s binding in a context.").requiredOption("--principal <p>", "principal: 'usr_<userId>'
|
|
14179
|
+
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
14180
|
addCommonAuthOptions(get).action(async (flags) => {
|
|
14109
14181
|
process.exit(await runAccessGet({ ...toCommonOptions(flags), principal: flags.principal, context: flags.context }, deps));
|
|
14110
14182
|
});
|