@vectros-ai/cli 0.16.0 → 0.20.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/README.md CHANGED
@@ -9,11 +9,15 @@ The Vectros provisioning CLI — the `vectros` binary:
9
9
  - **`vectros blueprint`** — author + inspect blueprints (`init` / `validate` / `plan` / `list`), creds-free.
10
10
  - **`vectros blueprint-test`** — apply a blueprint to a live env, assert it provisioned a working credential, then tear it down.
11
11
  - **`vectros context`** — create, list, and destroy app contexts.
12
+ - **`vectros issuers`** — register, update, delete, and inspect third-party IdP issuers (token-exchange trust anchors) for a context.
13
+ - **`vectros namespace`** — register a new scope namespace, and list/get existing ones.
12
14
  - **`vectros identity`** — manage tenant-wide principals (users, orgs, clients, and namespaces you register).
13
15
  - **`vectros role`** — define reusable, context-scoped access rules.
16
+ - **`vectros assume`** — switch identity namespace values on your own token, per a role's `assumable` grant.
14
17
  - **`vectros access`** — bind principals to contexts, and inspect what a binding grants.
15
18
  - **`vectros key`** — issue, list, revoke, and rotate scoped API keys (`ssk_*`).
16
19
  - **`vectros keyring`** — hold several identities on one machine, pick which is active, and let other tools resolve keys from one place.
20
+ - **`vectros join`** — enroll a principal in a context and keep the resulting credential in the keyring, in one step.
17
21
 
18
22
  ```bash
19
23
  npx -y @vectros-ai/cli bootstrap # provision a credential
@@ -144,6 +148,63 @@ vectros context destroy my-app # irreversible — asks you to r
144
148
  All four accept `--json` for machine-readable output, plus the common auth
145
149
  flags (`--base-url`, `--token`).
146
150
 
151
+ ## `vectros issuers` — registered third-party IdP issuers
152
+
153
+ Manage the third-party identity-provider registrations a context trusts for
154
+ [RFC 8693 token exchange](https://www.rfc-editor.org/rfc/rfc8693) — the
155
+ `issuer`/`jwksUri`/`audience` a blueprint's `issuers[]` entries register, plus
156
+ any self-signup policy attached to one. Everything a blueprint's `issuers:`
157
+ block can do is available standalone here too — adding a second IdP, or
158
+ editing one issuer's `selfSignupPolicies` after initial setup, no longer
159
+ requires hand-authoring a blueprint and re-running a full apply.
160
+
161
+ ```bash
162
+ vectros issuers register --context my-app --issuer-id auth0-prod \
163
+ --issuer https://your-tenant.us.auth0.com/ \
164
+ --jwks-uri https://your-tenant.us.auth0.com/.well-known/jwks.json \
165
+ --audience https://api.your-app.example.com
166
+ vectros issuers list --context my-app
167
+ vectros issuers get primary --context my-app
168
+ vectros issuers update primary --context my-app --status suspended
169
+ vectros issuers delete primary --context my-app # re-types the id to confirm
170
+ ```
171
+
172
+ | Subcommand | Flags | Notes |
173
+ |---|---|---|
174
+ | `register` | `--context`, `--issuer-id`, `--issuer`, `--jwks-uri`, `--audience` (all required); `--sub-claim`, `--email-claim`, `--userinfo-uri`, `--self-signup-policy <type:roleId>` (repeatable), `--tenant test\|live` | Idempotent by `--issuer-id` within the tenant — a repeat returns the existing registration unchanged, not an error. |
175
+ | `update <issuerId>` | `--context <contextId>` (required); `--sub-claim`, `--email-claim`, `--userinfo-uri`, `--status active\|suspended`, `--self-signup-policy <type:roleId>` (repeatable), `--clear-self-signup-policies`, `--tenant test\|live` | Only ever sends the SAFE, mutable fields — `issuer`/`jwks-uri`/`audience`/`contextId` are trust-anchor fields, immutable via this route on the platform side; rotating one is `delete` + `register`. |
176
+ | `delete <issuerId>` | `--context <contextId>` (required), `--force`, `--tenant test\|live` | Re-types the issuer id to confirm (mirrors `context destroy`); `--force` skips the prompt and is required when stdin isn't a TTY. Refused server-side while any user was ever created or matched through this issuer. |
177
+ | `list` | `--context <contextId>` (required), `--tenant test\|live` | Every issuer registered in that context. `--context` has no default — an issuer read is confined to the context you name; there's no tenant-wide "every issuer in every context" view. |
178
+ | `get <issuerId>` | `--context <contextId>` (required), `--tenant test\|live` | One issuer's full registration, including its self-signup policies. |
179
+
180
+ All five accept `--json`, plus the common auth flags (`--base-url`, `--token`).
181
+
182
+ ## `vectros namespace` — scope-namespace registrations
183
+
184
+ Register and inspect the namespaces a `scope:<namespace>` value can belong
185
+ to — whether values in that namespace must resolve to a real identity entity
186
+ (`entityBacked`, the same field `identity create --type <namespace>`
187
+ relies on) or are free-form strings. Registering a brand-new namespace (e.g.
188
+ a `practice`/`team` entity dimension) was blueprint-apply-only before this
189
+ command existed.
190
+
191
+ ```bash
192
+ vectros namespace register practice --context my-app --specificity-rank 1500 --entity-backed
193
+ vectros namespace list # tenant-wide registrations
194
+ vectros namespace list --context my-app # just my-app's own
195
+ vectros namespace get practice --context my-app
196
+ ```
197
+
198
+ | Subcommand | Flags | Notes |
199
+ |---|---|---|
200
+ | `register <name>` | `--context`, `--specificity-rank <n>` (both required); `--entity-backed`, `--default-schema-id <id>` | NOT idempotent — a repeat name 400s (unlike `issuers register`). `--context` has no tenant-wide option here — a confined credential may only register against its own bound context. |
201
+ | `list` | `--context <contextId>` (optional) | Omit for every tenant-wide registration; supply for one context's own. |
202
+ | `get <name>` | `--context <contextId>` (optional) | Same two-mode read as `list`. |
203
+
204
+ No `update`/`delete` — both routes require a genuine root API key on the
205
+ platform side, which this CLI never holds, so neither could ever succeed
206
+ here. All three subcommands accept `--json`.
207
+
147
208
  ## `vectros identity` — tenant-wide principals
148
209
 
149
210
  Manage the principals a context's access profiles and roles reference: users
@@ -158,6 +219,8 @@ vectros identity create --type org --external-id acme-inc --name "Acme Inc"
158
219
  vectros identity list --type user
159
220
  vectros identity get --type org --id <vectrosId>
160
221
  vectros identity delete --type org --id <vectrosId>
222
+ vectros identity invite --context my-app --role editor --email bob@example.com \
223
+ --accept-url https://my-app.example.com/accept
161
224
  ```
162
225
 
163
226
  | Subcommand | Flags | Notes |
@@ -166,11 +229,13 @@ vectros identity delete --type org --id <vectrosId>
166
229
  | | `--type user` only: `--email <email>`, `--service` (mint a SERVICE principal; default HUMAN) | `--name`/`--scope` are rejected for `--type user`. |
167
230
  | | entity namespaces only: `--name <name>`, `--scope <ns:value>` (repeatable, up to 2 parent ownership edges) | `--email`/`--service` are rejected for an entity namespace. |
168
231
  | | both: `--metadata <json>` | Free-form JSON object. |
169
- | `list` | `--type <user\|namespace>` (required), `--external-id <id>` (filter), `--limit <n>` | Omit `--limit` and the CLI drains every page for the complete set; pass `--limit` (1–100) and it returns exactly one page of that size instead. |
232
+ | `list` | `--type <user\|namespace>` (required), `--external-id <id>` (filter), `--limit <n>` | Scoped to your own context — see `list-all` below for a user created but never granted access anywhere. Omit `--limit` and the CLI drains every page for the complete set; pass `--limit` (1–100) and it returns exactly one page of that size instead. |
233
+ | `list-all` | `--external-id <id>` (filter), `--tenant test\|live`, `--limit <n>` | **Users only, and requires an account owner login.** Lists every user in the tenant, regardless of app-context access — for recovering a user `identity list` can never show you: one created but not (yet) granted access anywhere, so no context-scoped view can ever reach it, and a `create` retry with the same `--external-id` just collides with the same invisible row. Find it here by `--external-id`, then grant it access or `identity delete` it with an ordinary credential. |
170
234
  | `get` | `--type <user\|namespace>` (required), `--id <vectrosId>` (required) | `--id` is the Vectros-assigned id from `create`/`list`, not the `--external-id`. |
171
235
  | `delete` | `--type <user\|namespace>` (required), `--id <vectrosId>` (required) | |
236
+ | `invite` | `--context <c>`, `--role <roleId>`, `--email <e>` (all required); `--first-name <n>`, `--accept-url <url>`, `--no-send-email` | Onboards a person into a context by email (`POST /v1/users/invite`) — no root key required, your `vectros login` session already carries enough scope. `--role` is an existing Role's own id (no separate lookup). `--accept-url` is required unless `--no-send-email` is given, in which case the response carries the raw `inviteToken`/`acceptLink` for you to deliver yourself. |
172
237
 
173
- `create`/`list`/`get` accept `--json`; `delete` does not.
238
+ `create`/`list`/`list-all`/`get`/`invite` accept `--json`; `delete` does not.
174
239
 
175
240
  ## `vectros role` — reusable, context-scoped access rules
176
241
 
@@ -185,6 +250,8 @@ vectros role create --context my-app --role-id editor --name Editor \
185
250
  --actions records:cru,search:r
186
251
  vectros role create --context my-app --role-id own-org --name "Own org" \
187
252
  --actions records:r --data-scope '{"scope:org":["<orgId>", null]}'
253
+ vectros role create --context my-app --role-id hr-admin --name "HR Admin" \
254
+ --actions records:cru --assumable '{"scope:org":["org_engineering", "org_sales"]}'
188
255
  vectros role list --context my-app
189
256
  vectros role get --context my-app --role-id editor
190
257
  vectros role delete --context my-app --role-id editor
@@ -192,13 +259,35 @@ vectros role delete --context my-app --role-id editor
192
259
 
193
260
  | Subcommand | Flags | Notes |
194
261
  |---|---|---|
195
- | `create` | `--context <c>`, `--role-id <id>`, `--name <n>`, `--actions <csv>` (all required); `--data-scope <json>`, `--description <text>` | Idempotent by `roleId`. `--data-scope` confines the clause to owned data — a JSON map of ownership dimension → allowed values, ANDed across keys (e.g. `{"userId":["${{ self.userId }}"]}`, or `{"scope:org":["<orgId>", null]}` — the `null` entry additionally admits scope-less/tenant-shared records). Keys must be `userId` or `scope:<namespace>` (the legacy flat `orgId`/`clientId` keys are rejected server-side). |
262
+ | `create` | `--context <c>`, `--role-id <id>`, `--name <n>`, `--actions <csv>` (all required); `--data-scope <json>`, `--assumable <json>`, `--description <text>` | Idempotent by `roleId`. `--data-scope` confines the clause to owned data — a JSON map of ownership dimension → allowed values, ANDed across keys (e.g. `{"userId":["${{ self.userId }}"]}`, or `{"scope:org":["<orgId>", null]}` — the `null` entry additionally admits scope-less/tenant-shared records). Keys must be `userId` or `scope:<namespace>` (the legacy flat `orgId`/`clientId` keys are rejected server-side). `--assumable` is a SEPARATE, role-level grant — a JSON map of `scope:<namespace>` → allowed values naming which values a holder of this role may switch into via `vectros assume`; holding broad `--data-scope` reach in a namespace does not by itself grant assuming any value in it. |
196
263
  | `list` | `--context <c>` (required) | |
197
- | `get` | `--context <c>`, `--role-id <id>` (both required) | |
264
+ | `get` | `--context <c>`, `--role-id <id>` (both required) | Also shows the role's `assumable` grant, if any is set. |
198
265
  | `delete` | `--context <c>`, `--role-id <id>` (both required) | No cascade — any access profile still referencing the role fails to mint (not silently downgraded) until reassigned. |
199
266
 
200
267
  `create`/`list`/`get` accept `--json`; `delete` does not.
201
268
 
269
+ ## `vectros assume` — switch identity namespace values on your own token
270
+
271
+ Re-mints your own verb token with one or more `identity.<namespace>` values
272
+ switched — the "org switcher" flow, named (like the platform endpoint it
273
+ calls) after AWS STS `AssumeRole`. Only works if a role you currently hold
274
+ explicitly grants assuming the requested value(s) via that role's own
275
+ `--assumable` grant (checked live against your CURRENT roles, never a value
276
+ frozen into your token at mint time).
277
+
278
+ ```bash
279
+ vectros assume --context my-app --scope org:org_sales
280
+ ```
281
+
282
+ | Flags | Notes |
283
+ |---|---|
284
+ | `--context <c>` (required), `--scope <ns:value>` (repeatable, at least one required) | Each `--scope` becomes one `scope:<namespace>` entry in the request. No artificial cap on how many namespaces you name — the platform requires a SINGLE role to jointly grant the whole combination and rejects an unsatisfiable one itself. |
285
+
286
+ Prints the re-minted `access_token`/`expires_in` (accepts `--json`) — there
287
+ is no `--to-keyring` here, since the result is an ephemeral bearer token
288
+ with the same remaining lifetime as the verb token this command itself
289
+ mints to make the call, not a durable secret the keyring is built to hold.
290
+
202
291
  ## `vectros access` — bind principals to contexts
203
292
 
204
293
  The binding layer between an identity and a context: `access grant` joins a
@@ -224,14 +313,42 @@ shares this grammar.
224
313
 
225
314
  | Subcommand | Flags | Notes |
226
315
  |---|---|---|
227
- | `grant` | `--principal <p>`, `--context <c>` (required); exactly one of `--role <roleId>` / `--actions <csv>`; `--scope <ns:value>` (repeatable), `--identity-overrides <json>` | Idempotent **upsert** — re-granting an existing binding updates it to the newly-declared shape (role/actions/overrides), it does not no-op. `--scope`/`--identity-overrides` extend the principal's stamped identity (e.g. `--scope org:<id>` → `scope:org`) — a credential can only stamp ownership its identity carries, so granting write access to org-scoped data requires that scope on the profile. When both are given, `--identity-overrides` wins on key conflicts. |
316
+ | `grant` | `--principal <p>`, `--context <c>` (required); exactly one of `--role <roleId>` (repeatable — 2+ compose additively) / `--actions <csv>`; `--scope <ns:value>` (repeatable), `--identity-overrides <json>` | Idempotent **upsert** — re-granting an existing binding updates it to the newly-declared shape (role(s)/actions/overrides), it does not no-op. Repeating `--role` composes: `--role case-handler --role hr-admin` grants each role's own clauses, concatenated in the order given — never merged, so each clause keeps meaning exactly what its own author wrote. `--scope`/`--identity-overrides` extend the principal's stamped identity (e.g. `--scope org:<id>` → `scope:org`) — a credential can only stamp ownership its identity carries, so granting write access to org-scoped data requires that scope on the profile. When both are given, `--identity-overrides` wins on key conflicts. |
228
317
  | `revoke` | `--principal <p>`, `--context <c>` (required) | |
229
318
  | `list` | Exactly one of `--context <c>` / `--principal <p>` | `--context` lists the context's members; `--principal` lists every context that principal is bound to. |
230
319
  | `get` | `--principal <p>`, `--context <c>` (required) | |
231
- | `explain` | `--principal <p>`, `--context <c>` (required) | Resolves the binding's clauses (following its role, if bound via one) and renders a concrete **access matrix** — a canned set of record-ownership archetypes (yours alone, another user's, org-shared, scope-less, …) evaluated against the clauses. A client-side preview for authoring-time sanity, not the platform's own evaluator: it models READ reachability only — a `c`/`u`/`d` action shown as granted is advisory, not a guarantee a specific write will succeed, since the platform's write rule (a clause must explicitly name a dimension to place a value into it) diverges from its read rule (an unmentioned dimension never narrows reads). |
320
+ | `explain` | `--principal <p>`, `--context <c>` (required) | Resolves the binding's clauses (following every referenced role and concatenating their clauses, if bound via one or more) and renders a concrete **access matrix** — a canned set of record-ownership archetypes (yours alone, another user's, org-shared, scope-less, …) evaluated against the clauses. A client-side preview for authoring-time sanity, not the platform's own evaluator: it models READ reachability only — a `c`/`u`/`d` action shown as granted is advisory, not a guarantee a specific write will succeed, since the platform's write rule (a clause must explicitly name a dimension to place a value into it) diverges from its read rule (an unmentioned dimension never narrows reads). |
232
321
 
233
322
  `grant`/`list`/`get`/`explain` accept `--json`; `revoke` does not.
234
323
 
324
+ ## `vectros join` — enroll and keep the credential, in one step
325
+
326
+ `join` is a **composition verb**: everything it does already exists as a
327
+ discrete command (`access grant`, `key issue`, `keyring add`) — `join` runs
328
+ them in order against one context and records the outcome as a keyring
329
+ entry, so `vectros switch <alias>` (and the MCP server / recall hook) can act
330
+ as that principal from then on.
331
+
332
+ ```bash
333
+ vectros join my-app --role editor # enroll "me" via a role
334
+ vectros join my-app --role case-handler --role hr-admin # compose 2+ roles additively
335
+ vectros join my-app --actions records:cru,search:r --scope org:<orgId>
336
+ vectros join my-app --as usr_<id> --role editor --alias teammate
337
+ ```
338
+
339
+ | Flag | Notes |
340
+ |---|---|
341
+ | `--role <roleId>` | Repeatable — 2+ compose additively, same semantics as `access grant --role`. XOR with `--actions`. |
342
+ | `--actions <csv>` | Inline single-clause grant. XOR with `--role`. |
343
+ | `--as me\|usr_<id>` (default `me`) | Which principal to enroll — `me` resolves to your own signed-in user. |
344
+ | `--scope <ns:value>` | Repeatable. Places the member under a scope for this context (an identity override on the profile) so their key can WRITE that scope's data. |
345
+ | `--alias <name>` | Keyring entry name (defaults to the context id). |
346
+ | `--activate` / `--no-activate` | Force (or skip) making the freshly-stored key this machine's active identity. |
347
+
348
+ Re-joining an alias whose secret is still held locally is idempotent: it
349
+ re-grants (a no-op upsert) and re-activates without churning the key. Only
350
+ when the local secret is missing does it rotate to obtain a fresh one.
351
+
235
352
  ## `vectros key` — the scoped API key (`ssk_*`) lifecycle
236
353
 
237
354
  Mint, inspect, revoke, and rotate the scoped keys (`ssk_*`) that authenticate
@@ -368,9 +485,15 @@ already holds — those verbs can only disclose or redistribute access, never
368
485
  manufacture new reach. `profiles:c` (creating a profile for an arbitrary,
369
486
  caller-chosen principal name) still hard-rejects — **except** `profiles:c:self`,
370
487
  which the platform resolves from the credential's own identity at request
371
- time, never from anything the blueprint declares; a bare `profiles:c` or a
372
- literal `profiles:c:usr_<id>` naming another principal still hard-rejects,
373
- same as every other control-plane scope.
488
+ time, never from anything the blueprint declares, and **except a bare
489
+ `profiles:c` on a clause that also declares the `member-lifecycle` capability**
490
+ (see Capabilities, below) — that combination is admitted because
491
+ `member-lifecycle` already grants unbounded principal-creation reach, so
492
+ gating bare `profiles:c` behind it adds no further technical bound but keeps
493
+ the same explicit, fail-closed opt-in signal every other admission here
494
+ requires. A literal-qualified `profiles:c:usr_<id>` naming another principal
495
+ still hard-rejects regardless of `member-lifecycle`, same as every other
496
+ control-plane scope.
374
497
 
375
498
  ### Capabilities (`granted_capabilities`) — a separate, equally-gated dimension
376
499
 
@@ -378,10 +501,10 @@ A blueprint's `accessProfile` and role clauses may also declare
378
501
  `capabilities: [...]` — named platform effects (like creating or removing an
379
502
  identity in your tenant) that reach across a partition boundary in a way no
380
503
  `resource:ops` verb can express. This rides a field entirely separate from
381
- `allowedActions`, and it has its own allowlist: **only `member-lifecycle` is
382
- admitted today.** Every other capability name — including ones the platform's
383
- own registry recognizes — is refused, the same fail-closed, deliberately-widen
384
- posture as the scope gate above. `vectros blueprint validate`/`plan` and
504
+ `allowedActions`, and it has its own allowlist: **`member-lifecycle` and
505
+ `delegate-principal-stamp` are admitted today.** Every other capability name —
506
+ including ones the platform's own registry recognizes — is refused, the same
507
+ fail-closed, deliberately-widen posture as the scope gate above. `vectros blueprint validate`/`plan` and
385
508
  `bootstrap` all report a rejected capability the same way they report a
386
509
  rejected scope: nothing mints, and the CLI exits non-zero.
387
510
 
@@ -1,11 +1,11 @@
1
1
  # Third-party notices
2
2
 
3
3
  `@vectros-ai/cli` compiles the following third-party packages directly into its
4
- published binary (`dist/cli.js`). Their licenses require that these notices travel
5
- with the code, so they are reproduced in full below.
4
+ published bundle. Their licenses require that these notices travel with the code,
5
+ so they are reproduced in full below.
6
6
 
7
- This file is GENERATED by `pipelines/third_party_notices.mjs` from the `noExternal`
8
- lists in `packages/*/tsup.config.ts`. Do not edit it by hand — run
7
+ This file is GENERATED by `pipelines/third_party_notices.mjs` from `packages/cli/tsup.config.ts`'s
8
+ `noExternal` list. Do not edit it by hand — run
9
9
  `node pipelines/third_party_notices.mjs --write`. CI fails if it is stale, because a
10
10
  notices file that silently drifts is worse than none: it asserts a compliance that
11
11
  nobody checked.