@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/dist/cli.mjs CHANGED
@@ -9648,6 +9648,18 @@ var codingAgentMemory = {
9648
9648
  ]
9649
9649
  };
9650
9650
  var coding_agent_memory_default = codingAgentMemory;
9651
+ var DATA_PLANE_ACTIONS = [
9652
+ "records:r",
9653
+ "records:c",
9654
+ "records:u",
9655
+ "search:r",
9656
+ "schemas:r",
9657
+ "inference:r",
9658
+ "documents:r",
9659
+ "documents:c",
9660
+ "folders:r",
9661
+ "folders:c"
9662
+ ];
9651
9663
  var agenticSdlc = {
9652
9664
  name: "agentic-sdlc",
9653
9665
  version: "1.0.0",
@@ -10301,23 +10313,25 @@ var agenticSdlc = {
10301
10313
  ]
10302
10314
  }
10303
10315
  ],
10304
- // Least-privilege, data-plane only. r/c/u records + search + schema discovery +
10305
- // inference:r (grounded recall over the document bodies) + document/folder r/c
10306
- // (the content artifacts are documents). NO :d — knowledge is superseded/retired
10307
- // via a status flip, so the trail of how the team's thinking evolved stays intact.
10316
+ // Least-privilege, data-plane only. The scope of the `ssk_*` key the bootstrap
10317
+ // mints for THIS blueprint's service principal (the MCP/API runtime). See
10318
+ // DATA_PLANE_ACTIONS above for the action set + rationale.
10308
10319
  accessProfile: {
10309
- allowedActions: [
10310
- "records:r",
10311
- "records:c",
10312
- "records:u",
10313
- "search:r",
10314
- "schemas:r",
10315
- "inference:r",
10316
- "documents:r",
10317
- "documents:c",
10318
- "folders:r",
10319
- "folders:c"
10320
- ]
10320
+ allowedActions: DATA_PLANE_ACTIONS
10321
+ },
10322
+ // A reusable `editor` role for the HUMAN owner — DISTINCT from `accessProfile`
10323
+ // (which scopes only the service-principal key). `bootstrap` provisions this
10324
+ // role in the context but binds it to no one; the owner joins themselves so the
10325
+ // data-plane app (app.vectros.ai) shows their KB — its switcher lists only
10326
+ // contexts the signed-in user holds an active access profile in, and bootstrap
10327
+ // grants the human none by default. Bind it after bootstrap with:
10328
+ // vectros access grant --principal usr_<your-user-id> --context agentic-sdlc --role editor
10329
+ // (or the admin app's Access > Contexts > agentic-sdlc > Profiles > Create).
10330
+ // Editor PARITY with the service key (same DATA_PLANE_ACTIONS) so a human
10331
+ // curator can browse AND write/correct the KB; still no :d and no control-plane
10332
+ // action, so the scope gate accepts it exactly like the accessProfile.
10333
+ roles: {
10334
+ editor: [{ allowedActions: DATA_PLANE_ACTIONS }]
10321
10335
  },
10322
10336
  servicePrincipal: {
10323
10337
  externalId: "agentic-sdlc",
@@ -12602,12 +12616,63 @@ function splitCsv(csv) {
12602
12616
  return csv.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
12603
12617
  }
12604
12618
 
12619
+ // src/paginate.ts
12620
+ var PAGE_LIMIT = 100;
12621
+ async function drainPages(fetch2) {
12622
+ const out = [];
12623
+ let startFrom;
12624
+ for (; ; ) {
12625
+ const page = await fetch2(startFrom);
12626
+ if (page.data) out.push(...page.data);
12627
+ const next = page.nextCursor;
12628
+ if (!next) break;
12629
+ startFrom = next;
12630
+ }
12631
+ return out;
12632
+ }
12633
+
12634
+ // src/self-principal.ts
12635
+ var PRINCIPAL_PREFIX_USER = "usr_";
12636
+ function emailFromJwt(token) {
12637
+ const parts = token.split(".");
12638
+ if (parts.length < 2) return void 0;
12639
+ try {
12640
+ const payload = Buffer.from(parts[1], "base64url").toString("utf8");
12641
+ const claims = JSON.parse(payload);
12642
+ return typeof claims.email === "string" && claims.email.trim() ? claims.email.trim() : void 0;
12643
+ } catch {
12644
+ return void 0;
12645
+ }
12646
+ }
12647
+ async function resolveSelfPrincipal(client, cognitoToken) {
12648
+ const email = emailFromJwt(cognitoToken);
12649
+ if (!email) return null;
12650
+ const target = email.toLowerCase();
12651
+ const users = await drainPages(
12652
+ (startFrom) => client.identity.listUsers(
12653
+ startFrom === void 0 ? { limit: PAGE_LIMIT } : { startFrom, limit: PAGE_LIMIT }
12654
+ )
12655
+ );
12656
+ const match = users.find(
12657
+ (u) => u.id && typeof u.email === "string" && u.email.toLowerCase() === target
12658
+ );
12659
+ if (!match?.id) return null;
12660
+ return { principalId: `${PRINCIPAL_PREFIX_USER}${match.id}`, userId: match.id, email };
12661
+ }
12662
+
12605
12663
  // src/commands/whoami.ts
12606
12664
  async function runWhoami(options, deps = {}) {
12607
12665
  return withAuth(options, deps, async (auth) => {
12608
12666
  const { identity, baseUrl } = auth;
12667
+ let self2 = null;
12668
+ try {
12669
+ self2 = await resolveSelfPrincipal(auth.client, auth.cognitoToken);
12670
+ } catch {
12671
+ self2 = null;
12672
+ }
12609
12673
  if (options.json) {
12610
- process.stdout.write(`${formatJson(identity)}
12674
+ const out = self2 ? { ...identity, userId: self2.userId, userPrincipalId: self2.principalId } : identity;
12675
+ process.stdout.write(`${formatJson(out)}
12611
12676
  `);
12612
12677
  return 0;
12613
12678
  }
@@ -12619,6 +12684,7 @@ async function runWhoami(options, deps = {}) {
12619
12684
  ["principal", identity.principalType],
12620
12685
  ["principal id", identity.principalKeyId],
12621
12686
  ["label", identity.principalLabel],
12687
+ ["your user id", self2?.principalId],
12622
12688
  ["allowed actions", identity.allowedActions?.length ? identity.allowedActions.join(", ") : void 0],
12623
12689
  ["token expires", expiry],
12624
12690
  ["api", baseUrl]
@@ -12628,21 +12694,6 @@ async function runWhoami(options, deps = {}) {
12628
12694
  });
12629
12695
  }
12630
12696
 
12631
- // src/paginate.ts
12632
- var PAGE_LIMIT = 100;
12633
- async function drainPages(fetch2) {
12634
- const out = [];
12635
- let startFrom;
12636
- for (; ; ) {
12637
- const page = await fetch2(startFrom);
12638
- if (page.data) out.push(...page.data);
12639
- const next = page.nextCursor;
12640
- if (!next) break;
12641
- startFrom = next;
12642
- }
12643
- return out;
12644
- }
12645
-
12646
12697
  // src/commands/key.ts
12647
12698
  var DEFAULT_KEY_NAME = "default";
12648
12699
  function emitMintedKey(key, format) {
@@ -13032,6 +13083,17 @@ function assertPrincipalShape(principal) {
13032
13083
  throw new Error(`--principal must be 'usr_<userId>' or 'key_<keyId>' (got '${principal}').`);
13033
13084
  }
13034
13085
  }
13086
+ var SELF_PRINCIPAL_ALIAS = "me";
13087
+ async function resolvePrincipalArg(raw, auth) {
13088
+ if (raw !== SELF_PRINCIPAL_ALIAS) return raw;
13089
+ const self2 = await resolveSelfPrincipal(auth.client, auth.cognitoToken);
13090
+ if (!self2) {
13091
+ throw new Error(
13092
+ "Could not resolve your own user id from your login. Pass --principal usr_<id> explicitly (find the id with `vectros identity list --type user`)."
13093
+ );
13094
+ }
13095
+ return self2.principalId;
13096
+ }
13035
13097
  function renderProfile(p) {
13036
13098
  return formatKeyValues([
13037
13099
  ["context", p.contextId],
@@ -13048,21 +13110,24 @@ async function runAccessGrant(opts, deps = {}) {
13048
13110
  process.stderr.write(" \u2716 Provide exactly one of --role <roleId> or --actions <csv>.\n");
13049
13111
  return 2;
13050
13112
  }
13051
- try {
13052
- assertPrincipalShape(opts.principal);
13053
- } catch (err) {
13054
- process.stderr.write(` \u2716 ${formatError(err)}
13113
+ if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
13114
+ try {
13115
+ assertPrincipalShape(opts.principal);
13116
+ } catch (err) {
13117
+ process.stderr.write(` \u2716 ${formatError(err)}
13055
13118
  `);
13056
- return 2;
13119
+ return 2;
13120
+ }
13057
13121
  }
13058
13122
  return withAuth(opts, deps, async (auth) => {
13059
13123
  const client = auth.client;
13060
13124
  try {
13125
+ const principal = await resolvePrincipalArg(opts.principal, auth);
13061
13126
  const scopes = hasActions ? [{ allowed_actions: splitCsv(opts.actions) }] : void 0;
13062
13127
  const profile = await client.auth.createAccessProfile({
13063
13128
  contextId: opts.context,
13064
13129
  body: {
13065
- principalId: opts.principal,
13130
+ principalId: principal,
13066
13131
  roleId: opts.role,
13067
13132
  scopes
13068
13133
  }
@@ -13074,7 +13139,7 @@ async function runAccessGrant(opts, deps = {}) {
13074
13139
  }
13075
13140
  process.stdout.write(
13076
13141
  `
13077
- \u2714 Granted ${opts.principal} in '${opts.context}'` + (hasRole ? ` via role '${opts.role}'` : ` with ${splitCsv(opts.actions).length} action(s)`) + `
13142
+ \u2714 Granted ${principal} in '${opts.context}'` + (hasRole ? ` via role '${opts.role}'` : ` with ${splitCsv(opts.actions).length} action(s)`) + `
13078
13143
  ${renderProfile(profile)}
13079
13144
  `
13080
13145
  );
@@ -13087,18 +13152,21 @@ ${renderProfile(profile)}
13087
13152
  });
13088
13153
  }
13089
13154
  async function runAccessRevoke(opts, deps = {}) {
13090
- try {
13091
- assertPrincipalShape(opts.principal);
13092
- } catch (err) {
13093
- process.stderr.write(` \u2716 ${formatError(err)}
13155
+ if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
13156
+ try {
13157
+ assertPrincipalShape(opts.principal);
13158
+ } catch (err) {
13159
+ process.stderr.write(` \u2716 ${formatError(err)}
13094
13160
  `);
13095
- return 2;
13161
+ return 2;
13162
+ }
13096
13163
  }
13097
13164
  return withAuth(opts, deps, async (auth) => {
13098
13165
  const client = auth.client;
13099
13166
  try {
13100
- await client.auth.deleteAccessProfile({ contextId: opts.context, principalId: opts.principal });
13101
- process.stdout.write(` \u2714 Revoked ${opts.principal} from '${opts.context}'
13167
+ const principal = await resolvePrincipalArg(opts.principal, auth);
13168
+ await client.auth.deleteAccessProfile({ contextId: opts.context, principalId: principal });
13169
+ process.stdout.write(` \u2714 Revoked ${principal} from '${opts.context}'
13102
13170
  `);
13103
13171
  return 0;
13104
13172
  } catch (err) {
@@ -13113,7 +13181,7 @@ async function runAccessList(opts, deps = {}) {
13113
13181
  process.stderr.write(" \u2716 Provide exactly one of --context <c> or --principal <p>.\n");
13114
13182
  return 2;
13115
13183
  }
13116
- if (opts.principal) {
13184
+ if (opts.principal && opts.principal !== SELF_PRINCIPAL_ALIAS) {
13117
13185
  try {
13118
13186
  assertPrincipalShape(opts.principal);
13119
13187
  } catch (err) {
@@ -13125,10 +13193,11 @@ async function runAccessList(opts, deps = {}) {
13125
13193
  return withAuth(opts, deps, async (auth) => {
13126
13194
  const client = auth.client;
13127
13195
  try {
13196
+ const principal = opts.principal ? await resolvePrincipalArg(opts.principal, auth) : void 0;
13128
13197
  const rows = opts.context ? await drainPages(
13129
13198
  (startFrom) => client.auth.listAccessProfiles({ contextId: opts.context, startFrom, limit: PAGE_LIMIT })
13130
13199
  ) : await drainPages(
13131
- (startFrom) => client.auth.listProfilesForPrincipal({ principalId: opts.principal, startFrom, limit: PAGE_LIMIT })
13200
+ (startFrom) => client.auth.listProfilesForPrincipal({ principalId: principal, startFrom, limit: PAGE_LIMIT })
13132
13201
  );
13133
13202
  if (opts.json) {
13134
13203
  process.stdout.write(`${formatJson(rows)}
@@ -13152,17 +13221,20 @@ async function runAccessList(opts, deps = {}) {
13152
13221
  });
13153
13222
  }
13154
13223
  async function runAccessGet(opts, deps = {}) {
13155
- try {
13156
- assertPrincipalShape(opts.principal);
13157
- } catch (err) {
13158
- process.stderr.write(` \u2716 ${formatError(err)}
13224
+ if (opts.principal !== SELF_PRINCIPAL_ALIAS) {
13225
+ try {
13226
+ assertPrincipalShape(opts.principal);
13227
+ } catch (err) {
13228
+ process.stderr.write(` \u2716 ${formatError(err)}
13159
13229
  `);
13160
- return 2;
13230
+ return 2;
13231
+ }
13161
13232
  }
13162
13233
  return withAuth(opts, deps, async (auth) => {
13163
13234
  const client = auth.client;
13164
13235
  try {
13165
- const p = await client.auth.getAccessProfile({ contextId: opts.context, principalId: opts.principal });
13236
+ const principal = await resolvePrincipalArg(opts.principal, auth);
13237
+ const p = await client.auth.getAccessProfile({ contextId: opts.context, principalId: principal });
13166
13238
  if (opts.json) {
13167
13239
  process.stdout.write(`${formatJson(p)}
13168
13240
  `);
@@ -13938,9 +14010,9 @@ async function runBlueprintTest(fileOrName, opts, deps = {}) {
13938
14010
 
13939
14011
  // src/build-info.ts
13940
14012
  var BUILD_INFO = {
13941
- cli: true ? "0.6.4" : "dev",
14013
+ cli: true ? "0.6.5" : "dev",
13942
14014
  sdk: true ? "0.31.0" : "dev",
13943
- blueprints: true ? "0.6.2" : "dev"
14015
+ blueprints: true ? "0.6.3" : "dev"
13944
14016
  };
13945
14017
  function formatBuildInfo() {
13946
14018
  return `vectros ${BUILD_INFO.cli} (sdk ${BUILD_INFO.sdk}, blueprints ${BUILD_INFO.blueprints})`;
@@ -14059,7 +14131,7 @@ function registerRoleCommands(program, deps) {
14059
14131
  }
14060
14132
  function registerAccessCommands(program, deps) {
14061
14133
  const access2 = program.command("access").description("Manage access bindings: principal \u2194 context \u2194 role/scopes.");
14062
- 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)");
14134
+ 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)");
14063
14135
  addCommonAuthOptions(grant).action(
14064
14136
  async (flags) => {
14065
14137
  process.exit(
@@ -14073,15 +14145,15 @@ function registerAccessCommands(program, deps) {
14073
14145
  );
14074
14146
  }
14075
14147
  );
14076
- 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");
14148
+ 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");
14077
14149
  addAuthOptions(revoke).action(async (flags) => {
14078
14150
  process.exit(await runAccessRevoke({ ...toCommonOptions(flags), principal: flags.principal, context: flags.context }, deps));
14079
14151
  });
14080
- 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");
14152
+ 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)");
14081
14153
  addCommonAuthOptions(list).action(async (flags) => {
14082
14154
  process.exit(await runAccessList({ ...toCommonOptions(flags), context: flags.context, principal: flags.principal }, deps));
14083
14155
  });
14084
- 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");
14156
+ 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");
14085
14157
  addCommonAuthOptions(get).action(async (flags) => {
14086
14158
  process.exit(await runAccessGet({ ...toCommonOptions(flags), principal: flags.principal, context: flags.context }, deps));
14087
14159
  });