@vectros-ai/cli 0.12.0 → 0.14.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/AUTHORING.md +64 -9
- package/CHANGELOG.md +149 -0
- package/README.md +3 -1
- package/dist/cli.js +5031 -4351
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +5032 -4352
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
package/AUTHORING.md
CHANGED
|
@@ -78,7 +78,11 @@ A name must be 2–50 chars: a lowercase letter, then lowercase letters / digits
|
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
`bootstrap` shows the same plan and asks for confirmation before minting
|
|
81
|
-
(`--yes` skips the prompt for CI/agents
|
|
81
|
+
(`--yes` skips the prompt for CI/agents). ⚠️ **`--no-merge` is NOT a dry run** — it still
|
|
82
|
+
mints/reconciles for real; it only changes where the resulting credential goes, printing the MCP
|
|
83
|
+
config snippet to stdout instead of merging it into your client's config file. To preview without
|
|
84
|
+
touching your account, stop at `blueprint plan` (offline) — `bootstrap`, with or without `--no-merge`,
|
|
85
|
+
always applies.
|
|
82
86
|
|
|
83
87
|
**Provisioning a production context — `--no-seed`.** A blueprint's `seed`
|
|
84
88
|
records are illustrative sample data: handy for a getting-started or demo
|
|
@@ -120,7 +124,10 @@ contextId: mcp # 3-31 chars, lowercase-start. The app-context the key b
|
|
|
120
124
|
schemas:
|
|
121
125
|
- typeName: thing # immutable after first apply
|
|
122
126
|
displayName: Thing
|
|
123
|
-
indexMode: HYBRID # HYBRID
|
|
127
|
+
indexMode: HYBRID # HYBRID | SEMANTIC | TEXT | NONE (store-only).
|
|
128
|
+
# Omitting it does NOT mean HYBRID: a record schema with no
|
|
129
|
+
# indexMode resolves to NONE, and a document schema with none
|
|
130
|
+
# is rejected. Declare the mode you want.
|
|
124
131
|
fields:
|
|
125
132
|
- fieldId: title
|
|
126
133
|
fieldType: string # string | number | boolean | date | enum | array | object | reference
|
|
@@ -134,7 +141,9 @@ schemas:
|
|
|
134
141
|
required: true
|
|
135
142
|
lookupFields: [title] # extra O(1) exact-match lookup indexes (max 10);
|
|
136
143
|
# object form { fieldName, unique?, rangeEnabled?,
|
|
137
|
-
# sortBy?, allowOverflow? } for constraints/range
|
|
144
|
+
# sortBy?, allowOverflow? } for constraints/range, or
|
|
145
|
+
# { fieldNames: [a, b], sortBy?, allowOverflow? } for
|
|
146
|
+
# a composite (conjunctive) lookup over 2-3 fields.
|
|
138
147
|
# externalId has a built-in finder — don't list it.
|
|
139
148
|
capabilities: { auditHistory: true } # default true; surfaced for a self-documenting audit posture
|
|
140
149
|
|
|
@@ -191,12 +200,15 @@ before customers build on the schema. Three knobs, each permanent:
|
|
|
191
200
|
set `allowOverflow: true` on it, which accepts a higher-cost secondary index.
|
|
192
201
|
Keep equality lookups lean.
|
|
193
202
|
|
|
194
|
-
- **`sortBy` — the order results come back in
|
|
195
|
-
`sortBy` sets the listing order:
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
- **`sortBy` — the order results come back in, and what a sort-key window
|
|
204
|
+
narrows.** For an *equality* lookup, `sortBy` sets the listing order:
|
|
205
|
+
`createdAt` (default), `lastUpdated`, or a declared field. It is also what
|
|
206
|
+
`sortFrom`/`sortTo` bound when you narrow a `value` lookup. An **optional**
|
|
207
|
+
field is fine: records that don't carry it are listed ahead of those that do,
|
|
208
|
+
and a bounded window never includes them. *Locked:* `sortBy` itself AND the
|
|
209
|
+
sorted field's declared `fieldType` are both migration-locked — pick a new field
|
|
210
|
+
name if you need to change either. An `array`/`object` field is rejected as a
|
|
211
|
+
`sortBy` target (no meaningful order).
|
|
200
212
|
|
|
201
213
|
**Sensitive fields can still be looked up — by exact match only.** A
|
|
202
214
|
`sensitive` field is HMAC'd into a per-tenant **blind index**, so you *can* list
|
|
@@ -205,6 +217,49 @@ without the value ever being stored in the clear or entering the search index. A
|
|
|
205
217
|
blind hash is not orderable, so a sensitive lookup **cannot** be `rangeEnabled`,
|
|
206
218
|
and no lookup's `sortBy` may name a sensitive field (the platform rejects both).
|
|
207
219
|
|
|
220
|
+
### Composite (conjunctive) lookups — matching several fields at once
|
|
221
|
+
|
|
222
|
+
`{ fieldNames: [a, b] }` (2-3 fields) matches ALL of them together in one clause —
|
|
223
|
+
"every record where `status` is `open` **and** `area` is `billing`" — exact,
|
|
224
|
+
complete, in the declared order. This is a different query shape from declaring
|
|
225
|
+
`status` and `area` as two separate equality lookups: two separate lookups can
|
|
226
|
+
each tell you "records with this status" or "records with this area", but never
|
|
227
|
+
"records with both", short of fetching one side and filtering client-side.
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
lookupFields:
|
|
231
|
+
- externalId
|
|
232
|
+
- { fieldNames: [status, area], sortBy: createdAt, allowOverflow: true }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Like a single-field lookup, the shape is **migration-locked** at create — the same
|
|
236
|
+
"decide deliberately" rule as above applies, doubly so here since a composite
|
|
237
|
+
also can't be swapped for two plain lookups later without a new field name.
|
|
238
|
+
Rules the platform enforces, all offline-checkable and caught by `blueprint
|
|
239
|
+
validate` before they'd 400 at `bootstrap`:
|
|
240
|
+
|
|
241
|
+
- **2-3 fields, order significant.** A query may match a leading run of the list
|
|
242
|
+
(the first field alone, the first two together, …) but never a later field
|
|
243
|
+
alone — declare a separate lookup for that. No leg may repeat.
|
|
244
|
+
- **Each leg must be a scalar, single-valued field** — not `array`/`object`, and
|
|
245
|
+
not a `many`-cardinality `reference`. A leg occupies one sort-key position and
|
|
246
|
+
holds one value; a multi-valued field there would index only one rendering and
|
|
247
|
+
the conjunction would silently return a subset while looking complete.
|
|
248
|
+
Cardinality-`one` references are fine.
|
|
249
|
+
- **A leg can't ALSO be its own `sortBy` target**, on this same lookup or a
|
|
250
|
+
separate one that treats that field as `rangeEnabled`. Within a fixed tuple
|
|
251
|
+
every matching record has the same value for that leg, so ordering by it (or
|
|
252
|
+
range-querying it) is meaningless — pick a different field, `createdAt`, or
|
|
253
|
+
`lastUpdated`.
|
|
254
|
+
- **`unique`/`rangeEnabled` are not available on a composite** — it's an
|
|
255
|
+
exact-match index over its fields; declare a range lookup separately if you
|
|
256
|
+
need one. `sortBy` and `allowOverflow` ARE available; `sortBy` on a composite
|
|
257
|
+
orders *within* a group when a query supplies fewer values than the lookup
|
|
258
|
+
declares, not across the whole result.
|
|
259
|
+
- **Record-only.** The schema's `allowedSurfaces` must be omitted or exactly
|
|
260
|
+
`[record]` — the platform's composite index has no document/user/entity reader
|
|
261
|
+
yet.
|
|
262
|
+
|
|
208
263
|
### Reference fields — typed links between record types
|
|
209
264
|
|
|
210
265
|
A `reference` field is a foreign key to another record. It needs **both** the
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,155 @@
|
|
|
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.14.0
|
|
7
|
+
|
|
8
|
+
### Security
|
|
9
|
+
|
|
10
|
+
- **`bootstrap` no longer stores your minted `ssk_*` in plaintext.** The key
|
|
11
|
+
goes to this machine's OS credential store (or an encrypted file where none
|
|
12
|
+
is available); your MCP client config references it by alias only
|
|
13
|
+
(`VECTROS_KEYRING_ALIAS`), never the raw secret. **Breaking:** a config
|
|
14
|
+
merged by an older CLI keeps working, but a fresh `bootstrap` run now
|
|
15
|
+
produces the alias-only shape — if you scripted against the printed
|
|
16
|
+
snippet's `VECTROS_API_KEY` field, it's `VECTROS_KEYRING_ALIAS` now.
|
|
17
|
+
|
|
18
|
+
- **`--print` is renamed `--no-merge`, with no back-compat alias.** It was
|
|
19
|
+
never a dry run — it always applies for real and only changes where the
|
|
20
|
+
result goes. It still applies; it just skips writing your client's config
|
|
21
|
+
file. For a true preview, use `blueprint plan`.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`key issue --to-keyring` and `key rotate --to-keyring` store the minted secret
|
|
26
|
+
instead of printing it.** Until now the mint verbs could only *emit* a key, so on
|
|
27
|
+
a desktop the only place to put it was the MCP client's config file, in plaintext.
|
|
28
|
+
`--to-keyring` writes it to this machine's OS credential store where one is in use,
|
|
29
|
+
and to the encrypted file under `~/.vectros/secrets` otherwise — telling you which
|
|
30
|
+
of the two took it either way, because only one of them is login-gated. The key is
|
|
31
|
+
never printed to a terminal, a pipe or a transcript. Anything that resolves a
|
|
32
|
+
credential through `vectros keyring show --format raw` — the MCP server included
|
|
33
|
+
— then reads it with no `VECTROS_API_KEY` set anywhere.
|
|
34
|
+
|
|
35
|
+
`--alias <name>` names the entry (default: the context id) and `--no-activate`
|
|
36
|
+
stores it without making it the active identity. Emitting stays the **default**:
|
|
37
|
+
it is the right behaviour for a headless server, where a secrets manager
|
|
38
|
+
distributes the key and nothing should persist it locally. The two destinations
|
|
39
|
+
are mutually exclusive — `--to-keyring` with `--format raw|env|json` is rejected
|
|
40
|
+
before anything is minted, rather than storing the key and printing it anyway.
|
|
41
|
+
|
|
42
|
+
`rotate --to-keyring` proves the store will accept a write *before* it revokes
|
|
43
|
+
the old key, so a machine that cannot hold a secret fails with your existing
|
|
44
|
+
credential intact.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **`bootstrap --plan` no longer claims `HYBRID` for a schema that declares no
|
|
49
|
+
`indexMode`.** The plan rendered an absent mode as `HYBRID`, but nothing
|
|
50
|
+
substitutes that default — the value is forwarded untouched, and the platform
|
|
51
|
+
then resolves an absent mode to `NONE` (store-only, never searchable) on a record
|
|
52
|
+
schema, and rejects a document schema that declares none. So the plan promised a
|
|
53
|
+
searchable type and the apply produced a store-only one, or previewed a request
|
|
54
|
+
the API would refuse. The plan now names the real outcome in both cases.
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
- **A blueprint may now declare `indexMode: NONE`** (store-only: kept and looked up, never
|
|
59
|
+
searchable). The mode has always been accepted by the platform, and `bootstrap`/`validate`/`plan`
|
|
60
|
+
rejected it earlier than that — structural validation runs against this CLI's own bundled copy of
|
|
61
|
+
the blueprint format, which predated the value. That copy now carries it, so a blueprint declaring
|
|
62
|
+
`NONE` validates and applies. This governs blueprints loaded **from a file** as much as bundled
|
|
63
|
+
ones: both funnel through the same structural parse.
|
|
64
|
+
|
|
65
|
+
- **`blueprint init` scaffold and `AUTHORING.md`** document the full `indexMode` set including
|
|
66
|
+
`NONE`, and warn that omitting the field does not mean `HYBRID` — a record schema with no mode of
|
|
67
|
+
its own resolves to `NONE`, and a document schema with none is rejected.
|
|
68
|
+
|
|
69
|
+
## 0.13.0
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **`access explain` shows the row for "this record IS the identity you asked about".** A clause
|
|
74
|
+
bounding a namespace that belongs to an identity entity — a value the platform derives from the
|
|
75
|
+
entity's own id rather than one stored on the record — previously produced no matrix row for it at
|
|
76
|
+
all, because the matrix only built rows from values a clause spelled out literally. The matrix now
|
|
77
|
+
always carries that row, so a clause confining a credential to its own entity can be checked the same
|
|
78
|
+
way as every other ownership shape.
|
|
79
|
+
- **`access explain`'s grant matrix shows exactly which record types a row reaches, not only whether
|
|
80
|
+
it's granted.** A new "record types" column (`recordTypes` in `--json` output) names the types a
|
|
81
|
+
row's granting clause(s) actually cover — every type, a specific list, or none. Previously an
|
|
82
|
+
ownership-unscoped but type-restricted clause (a curated shared-content role, for example) rendered as
|
|
83
|
+
a plain "granted", which read as reaching every record type sharing that ownership shape, including
|
|
84
|
+
ones the clause never actually named.
|
|
85
|
+
- **`access explain` understands the placement matchers.** `${{ any }}` renders as `any` and matches
|
|
86
|
+
any value present in that dimension — deliberately *not* a record with no value there, since it does
|
|
87
|
+
not subsume the `null` sentinel (`[${{ any }}]` and `[${{ any }}, null]` are different grants).
|
|
88
|
+
`${{ under.self.* }}` renders as `under-self`; deciding it requires the parent edge of every
|
|
89
|
+
candidate value, which only the server has, so the matrix reports it as undecidable rather than
|
|
90
|
+
guessing. Previously both were compared as literal strings, so a clause using either was shown as
|
|
91
|
+
matching only a record whose owner value was the placeholder text itself.
|
|
92
|
+
- **New lint rule `datascope-unknown-placeholder` (error).** A `${{ … }}` value in a `dataScope` that
|
|
93
|
+
is not one of the platform's placeholders is now caught offline, with the correct spellings in the
|
|
94
|
+
message, instead of surfacing as a `400` at `apply`. Blueprint-level tokens (`${{ identities.* }}`,
|
|
95
|
+
`${{ inputs.* }}`, …) are substituted before the value reaches the platform and are not flagged.
|
|
96
|
+
- **A blueprint schema can now declare a composite (conjunctive) lookup** — `lookupFields: [{ fieldNames:
|
|
97
|
+
['status', 'area'] }]` matches on several fields at once ("every record where `status` is `open` **and**
|
|
98
|
+
`area` is `billing`"), 2-3 fields, exact and complete, in the declared field order. `blueprint validate`
|
|
99
|
+
now catches every leg-shape mistake the platform would otherwise 400 on mid-`bootstrap`: an undeclared
|
|
100
|
+
leg (naming the exact leg, not just the first), a repeated leg, a leg that's `array`/`object`-typed or a
|
|
101
|
+
many-cardinality reference, a leg that's separately declared `rangeEnabled`, and a leg used as its own
|
|
102
|
+
`sortBy` target. `bootstrap`/`blueprint-test apply` forward a composite to the partner API unchanged, and
|
|
103
|
+
the approval plan (`bootstrap`'s "Apply this plan?" preview) now renders one as `(status+area)` rather
|
|
104
|
+
than dropping it from the lookup list. `unique`/`rangeEnabled` aren't available on a composite; `sortBy`
|
|
105
|
+
and `allowOverflow` are. No bundled blueprint declares one yet — adopting the shape in the bundled
|
|
106
|
+
library is a separate, deliberate content decision.
|
|
107
|
+
|
|
108
|
+
### Changed
|
|
109
|
+
|
|
110
|
+
- **`--scope` now validates the value locally, on every command that takes it** — `join`,
|
|
111
|
+
`access grant` and `identity create`. A scope value must be 1-128 characters, starting with a letter
|
|
112
|
+
or digit and otherwise containing only letters, digits, `_` and `-` — the grammar the API enforces,
|
|
113
|
+
because a scope value becomes part of a storage key. `--scope org:a:b:c` previously passed a
|
|
114
|
+
colon-bearing value straight through; it now fails immediately, naming the offending value, instead
|
|
115
|
+
of returning a server error later in the call.
|
|
116
|
+
- **`access grant --identity-overrides` validates its values too.** The JSON form wins on key
|
|
117
|
+
conflicts with `--scope`, so a value rejected on one flag could previously be reinstated by the other
|
|
118
|
+
in the same command. Both are now checked after the merge.
|
|
119
|
+
- **Dependency maintenance** — repinned the bundled `@vectros-ai/sdk` to `0.38.0` (composite/conjunctive
|
|
120
|
+
record lookups — a schema can now be looked up by 2-3 fields matched together in one clause, not just
|
|
121
|
+
one — plus the placement matchers `${{ any }}` / `${{ under.self.* }}`, the `"*"` dimension wildcard in
|
|
122
|
+
`data_scope`, and sealed pagination cursors) and the bundled `@vectros-ai/blueprints` to `0.10.0` (the
|
|
123
|
+
composite-lookup authoring format above). The CLI bundles both into its published binary, so this is a
|
|
124
|
+
rebuild against their current versions.
|
|
125
|
+
|
|
126
|
+
### Fixed
|
|
127
|
+
|
|
128
|
+
- **`access explain` no longer reports a fully-permissive scope as granting nothing.** A clause using
|
|
129
|
+
the dimension wildcard — `data_scope: {"*": [...]}`, which grants over every dimension the clause
|
|
130
|
+
does not name explicitly — was read as naming an *unrecognized* dimension, and an unrecognized
|
|
131
|
+
dimension kills a whole clause. The most permissive shape there is therefore rendered as an empty
|
|
132
|
+
matrix. `*` is now recognized, and it is applied only to the dimensions a record actually carries,
|
|
133
|
+
with an explicit entry for a dimension always taking precedence over it.
|
|
134
|
+
- **`access explain` now says plainly that its "granted?" and "actions" columns model reading a record,
|
|
135
|
+
not writing one.** A clause that doesn't mention an ownership dimension no longer authorizes placing a
|
|
136
|
+
value there, but it never narrowed reads to begin with — so a create/update verb next to a row is not
|
|
137
|
+
confirmation that a write with that row's exact ownership shape will succeed. The command's output
|
|
138
|
+
now says so directly instead of leaving it implicit.
|
|
139
|
+
- **`access explain` recognizes a `${{ self.scope.<ns> }}` / `${{ under.self.scope.<ns> }}` placeholder
|
|
140
|
+
for every namespace the platform accepts, not just letters and dots.** A namespace containing a digit,
|
|
141
|
+
`_`, or `-` (`${{ self.scope.eng_team }}`) previously fell through to being matched as an opaque
|
|
142
|
+
literal string instead of resolving to the caller, silently under-reporting that clause's grant.
|
|
143
|
+
- **Corrected the `sortBy` guidance in `AUTHORING.md`.** It carried a "trap"
|
|
144
|
+
warning that sorting an equality lookup by an **optional** payload field
|
|
145
|
+
"silently drops records that don't have it." That was never accurate — an
|
|
146
|
+
equality lookup resolves on the partition key, and `sortBy` only orders within
|
|
147
|
+
it — and records with no value for the sorted field now take their own ordered
|
|
148
|
+
position ahead of the rest, and are never inside a `sortFrom`/`sortTo` window.
|
|
149
|
+
That ordering applies to records written from this platform release onward;
|
|
150
|
+
earlier records take their new position once they are next updated.
|
|
151
|
+
The section now also names what `sortBy` really locks: the sorted field's
|
|
152
|
+
declared `fieldType` is migration-locked alongside `sortBy` itself, and an
|
|
153
|
+
`array`/`object` field is rejected as a `sortBy` target.
|
|
154
|
+
|
|
6
155
|
## 0.12.0
|
|
7
156
|
|
|
8
157
|
### Added
|
package/README.md
CHANGED
|
@@ -115,7 +115,9 @@ The app-context is intentionally **not** deleted (it's idempotent and reused).
|
|
|
115
115
|
A machine can hold several Vectros identities (one per context or principal).
|
|
116
116
|
The keyring stores them as named entries plus one **active** pointer, so you can
|
|
117
117
|
switch between them without re-issuing keys. `vectros join <context>` adds an
|
|
118
|
-
entry and activates it; `vectros
|
|
118
|
+
entry and activates it; `vectros key issue`/`key rotate` add one when passed
|
|
119
|
+
`--to-keyring` (which stores the new secret instead of printing it); `vectros switch
|
|
120
|
+
<alias>` flips the active one.
|
|
119
121
|
|
|
120
122
|
```bash
|
|
121
123
|
vectros keyring list # the entries; * marks the active one
|