@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 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. r/c/u records + search + schema discovery +
10328
- // inference:r (grounded recall over the document bodies) + document/folder r/c
10329
- // (the content artifacts are documents). NO :d — knowledge is superseded/retired
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
- "records:r",
10334
- "records:c",
10335
- "records:u",
10336
- "search:r",
10337
- "schemas:r",
10338
- "inference:r",
10339
- "documents:r",
10340
- "documents:c",
10341
- "folders:r",
10342
- "folders:c"
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
- process.stdout.write(`${formatJson(identity)}
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
- try {
13075
- assertPrincipalShape(opts.principal);
13076
- } catch (err) {
13077
- process.stderr.write(` \u2716 ${formatError(err)}
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
- return 2;
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: opts.principal,
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 ${opts.principal} in '${opts.context}'` + (hasRole ? ` via role '${opts.role}'` : ` with ${splitCsv(opts.actions).length} action(s)`) + `
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
- try {
13114
- assertPrincipalShape(opts.principal);
13115
- } catch (err) {
13116
- process.stderr.write(` \u2716 ${formatError(err)}
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
- return 2;
13184
+ return 2;
13185
+ }
13119
13186
  }
13120
13187
  return withAuth(opts, deps, async (auth) => {
13121
13188
  const client = auth.client;
13122
13189
  try {
13123
- await client.auth.deleteAccessProfile({ contextId: opts.context, principalId: opts.principal });
13124
- process.stdout.write(` \u2714 Revoked ${opts.principal} from '${opts.context}'
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: opts.principal, startFrom, limit: PAGE_LIMIT })
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
- try {
13179
- assertPrincipalShape(opts.principal);
13180
- } catch (err) {
13181
- process.stderr.write(` \u2716 ${formatError(err)}
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
- return 2;
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 p = await client.auth.getAccessProfile({ contextId: opts.context, principalId: opts.principal });
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.4" : "dev",
14036
+ cli: true ? "0.6.5" : "dev",
13965
14037
  sdk: true ? "0.31.0" : "dev",
13966
- blueprints: true ? "0.6.2" : "dev"
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>' or 'key_<keyId>'").requiredOption("--context <c>", "app context").option("--role <roleId>", "reference a Role (XOR --actions)").option("--actions <csv>", "inline single-clause actions, comma-separated (XOR --role)");
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>' or 'key_<keyId>'").requiredOption("--context <c>", "app context");
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>' or 'key_<keyId>'").requiredOption("--context <c>", "app context");
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
  });