@headband/cli 0.3.0 → 0.6.1
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/LICENSE +21 -0
- package/README.md +290 -53
- package/dist/index.js +2700 -512
- package/package.json +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ZVN DEV
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @headband/cli
|
|
2
2
|
|
|
3
3
|
Command-line tools for [Headband](https://headband.dev): project setup, login,
|
|
4
|
-
API keys, indexes, documents, search, tasks, dedicated engines, schema
|
|
4
|
+
branches, API keys, indexes, documents, search, tasks, dedicated engines, schema
|
|
5
5
|
migrations and Sync. Every command works for a person at a terminal and for a
|
|
6
6
|
script or coding agent with no terminal at all.
|
|
7
7
|
|
|
@@ -26,6 +26,9 @@ npx @headband/cli login
|
|
|
26
26
|
--token <key> API key for this call. Wins over HEADBAND_API_KEY and stored logins.
|
|
27
27
|
--profile <name> Stored login to use (else HEADBAND_PROFILE, else the current profile)
|
|
28
28
|
--url <url> Headband URL (else HEADBAND_URL, else the profile's, else https://headband.dev)
|
|
29
|
+
--branch <name> Work on this branch for one call, in the project from --project <id>,
|
|
30
|
+
else the checkout's (see Branches). A branch id (br_...) is sent as it is.
|
|
31
|
+
Without --branch or a checkout, calls reach the primary branches.
|
|
29
32
|
--json Print one JSON document on stdout; errors print one JSON object on stderr
|
|
30
33
|
--yes, -y Answer yes to confirmations (required when stdin is not a terminal)
|
|
31
34
|
--help, -h Show help for the CLI or a command
|
|
@@ -102,9 +105,10 @@ headband indexes list --json
|
|
|
102
105
|
```
|
|
103
106
|
|
|
104
107
|
Which key to use: an admin key from `headband keys create <name> --type admin`
|
|
105
|
-
covers indexes, documents, search, engines and Sync. Managing keys, projects
|
|
106
|
-
and schema state needs a key minted by `headband login`, because only
|
|
107
|
-
carry `keys.manage`, `projects.manage
|
|
108
|
+
covers indexes, documents, search, engines and Sync. Managing keys, projects,
|
|
109
|
+
branches and schema state needs a key minted by `headband login`, because only
|
|
110
|
+
those carry `keys.manage`, `projects.manage`, `branches.manage` and
|
|
111
|
+
`schema.manage`.
|
|
108
112
|
|
|
109
113
|
## Scripts and agents
|
|
110
114
|
|
|
@@ -128,6 +132,11 @@ The CLI never waits for input it cannot get.
|
|
|
128
132
|
`network_error`. `status` is the HTTP status, or `null` when no request was
|
|
129
133
|
made.
|
|
130
134
|
|
|
135
|
+
- **Rate limits.** A `429` whose `Retry-After` is 10 seconds or less is
|
|
136
|
+
retried up to twice after that wait (CLI 0.6.0). If it still fails, the
|
|
137
|
+
command exits `7` and the message keeps the server's hint, such as
|
|
138
|
+
"Retry after 1s.". A write where some documents were rejected names the
|
|
139
|
+
first rejected document and the engine's reason.
|
|
131
140
|
- **Branch on exit codes**, not on message text. See the table below.
|
|
132
141
|
|
|
133
142
|
## Exit codes
|
|
@@ -135,7 +144,7 @@ The CLI never waits for input it cannot get.
|
|
|
135
144
|
| Code | Meaning | Typical cause |
|
|
136
145
|
| ----- | ---------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
137
146
|
| `0` | Success | |
|
|
138
|
-
| `1` | Error | Anything not listed below; also `schema diff` when changes are pending
|
|
147
|
+
| `1` | Error | Anything not listed below; also `schema diff` when changes are pending, `sync reconcile` when drift is left |
|
|
139
148
|
| `2` | Usage | Unknown command or option, missing or malformed value, HTTP 400/413/422, confirmation needed without a terminal |
|
|
140
149
|
| `3` | Authentication | No key found, a `--profile` that does not exist, HTTP 401 |
|
|
141
150
|
| `4` | Permission | HTTP 403, or 402 (the plan does not allow it) |
|
|
@@ -163,27 +172,144 @@ uses defaults. Running `headband` with no command runs `init`.
|
|
|
163
172
|
```bash
|
|
164
173
|
headband projects list [--json]
|
|
165
174
|
headband projects create <name> --engine <meilisearch|typesense|elasticsearch|visor> [--json]
|
|
166
|
-
headband projects delete <id> [--
|
|
175
|
+
headband projects delete <id> [--yes] [--json]
|
|
167
176
|
```
|
|
168
177
|
|
|
169
178
|
`projects delete` refuses a project that still has indexes (exit `6`, code
|
|
170
|
-
`
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
`project_not_empty`). Delete its indexes first. Deleting a project
|
|
180
|
+
also deletes the API keys scoped to it.
|
|
181
|
+
|
|
182
|
+
### Branches
|
|
183
|
+
|
|
184
|
+
A branch is one environment of a project: its primary branch `main`
|
|
185
|
+
(production), plus others such as `staging` or `preview/pr-123`. Each branch
|
|
186
|
+
has its own indexes and its own keys.
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
headband branch list [--project <id>] [--json]
|
|
190
|
+
headband branch create <name> --empty [--parent <name>] [--expires-in <2h|7d>] [--project <id>] [--json]
|
|
191
|
+
headband branch delete <name> [--project <id>] [--yes] [--json]
|
|
192
|
+
headband branch protect <name> [--project <id>] [--json]
|
|
193
|
+
headband branch unprotect <name> [--project <id>] [--json]
|
|
194
|
+
headband branch expire <name> (--in <2h|7d> | --at <iso> | --clear) [--project <id>] [--json]
|
|
195
|
+
headband branch checkout <name> [--project <id>] [--json]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
- The project comes from `--project`, else the directory's checkout (below).
|
|
199
|
+
With neither, the command is a usage error.
|
|
200
|
+
- `create` makes an empty branch. Without `--empty` it fails with exit `6`
|
|
201
|
+
(code `fork_not_available`): copying a parent's data into a new branch
|
|
202
|
+
arrives with bucket storage. Until branches other than main open on the
|
|
203
|
+
server, every `create` fails with exit `6` (code `branches_not_open`),
|
|
204
|
+
`--empty` or not. `--parent` records the branch it comes from
|
|
205
|
+
(default: the primary branch). `--expires-in` deletes the branch on its own
|
|
206
|
+
after that long.
|
|
207
|
+
- `delete` removes the branch with its indexes, keys and engines. It asks
|
|
208
|
+
first. The primary branch, a protected branch, a branch with live child
|
|
209
|
+
branches and a branch with Sync-owned indexes are refused (exit `6`).
|
|
210
|
+
- `protect` blocks delete and expiry; `unprotect` lifts it. The primary
|
|
211
|
+
branch (`main`) serves production and is always protected: `unprotect`,
|
|
212
|
+
`expire` and `delete` on it are refused (exit `6`, code `branch_is_primary`).
|
|
213
|
+
- `expire` takes exactly one of `--in <duration>`, `--at <ISO time>` (in the
|
|
214
|
+
future) or `--clear`.
|
|
215
|
+
- Durations are a number and a unit: `s`, `m`, `h`, `d` or `w`, such as `30m`,
|
|
216
|
+
`2h` or `7d`.
|
|
217
|
+
- Creating, deleting, protecting and expiring need the key from
|
|
218
|
+
`headband login`. `branch list` works with any admin key that reaches the
|
|
219
|
+
project.
|
|
220
|
+
|
|
221
|
+
**Working on a branch.** `checkout` makes a directory work on one branch:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
headband branch checkout staging --project <project id>
|
|
225
|
+
headband indexes list # staging's indexes
|
|
226
|
+
headband docs push products.json --index products
|
|
227
|
+
headband search products shoes # searches staging
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
`checkout` writes `.headband/project.json`: `{ "orgId", "projectId", "branch" }`,
|
|
231
|
+
with file mode `0600`. It holds no key. Every later command in that directory,
|
|
232
|
+
or in any directory below it, sends `Headband-Branch: <project id>/<branch>`
|
|
233
|
+
with your login key. That key reaches every branch of your organization, so no
|
|
234
|
+
key is minted. Checking out `main` writes the file too. `checkout` needs a
|
|
235
|
+
stored login (`headband login`), because the file records your login's
|
|
236
|
+
organization. `--token` and `HEADBAND_API_KEY` take the place of a stored
|
|
237
|
+
login, so unset them to check out.
|
|
238
|
+
|
|
239
|
+
Like git, the CLI uses the nearest `.headband/project.json` in the working
|
|
240
|
+
directory or a parent, up to your home directory (or the filesystem root,
|
|
241
|
+
outside it). `checkout` rewrites that nearest file, or writes one in the
|
|
242
|
+
working directory when there is none. As in git, a file another user owns, or
|
|
243
|
+
one in a `.headband` directory another user owns, is an error: every command
|
|
244
|
+
that would send `Headband-Branch` stops and names the file, and the search goes
|
|
245
|
+
no further. So a file planted in a shared directory such as `/tmp` never steers
|
|
246
|
+
your commands. A symlink is judged by its own owner, as in git, so another
|
|
247
|
+
user's link to your `.headband` is theirs. `checkout` then writes your own file
|
|
248
|
+
in the working directory, and never rewrites the other user's. When the other
|
|
249
|
+
user's file is in the working directory itself, remove it before you check out
|
|
250
|
+
there.
|
|
251
|
+
|
|
252
|
+
A checkout never falls back to production silently. Every command that would
|
|
253
|
+
send `Headband-Branch` stops with an error when the nearest
|
|
254
|
+
`.headband/project.json` cannot be read, holds a value that is not a valid
|
|
255
|
+
project id, branch name or branch id, or was written for another organization
|
|
256
|
+
than your stored login's. Run `headband branch checkout <name>` again to rewrite
|
|
257
|
+
the file, or delete it to work on production. A token (`--token`,
|
|
258
|
+
`HEADBAND_API_KEY`) names no organization, so the organization check applies
|
|
259
|
+
only to stored logins.
|
|
260
|
+
|
|
261
|
+
For one command, `--project <id> --branch <name>` picks another branch, and
|
|
262
|
+
`--branch <name>` alone takes the checkout's project. A branch id
|
|
263
|
+
(`--branch br_...`) names its own project, so it is sent as it is and needs no
|
|
264
|
+
`--project`. With no checkout and no `--branch`, commands reach each project's
|
|
265
|
+
primary branch, as before. A branch name with no project at all is a usage
|
|
266
|
+
error, so a name is never matched across projects by accident.
|
|
267
|
+
|
|
268
|
+
`--project` without `--branch` is a usage error, because these commands send no
|
|
269
|
+
project of their own and would ignore it. The exception is `keys create`, which
|
|
270
|
+
sends the project in its body (see Keys).
|
|
271
|
+
|
|
272
|
+
Coming with bucket storage: `branch create` without `--empty` (a copy of its
|
|
273
|
+
parent), `reset`, `rollback`, `restore` and `set-primary`.
|
|
174
274
|
|
|
175
275
|
### Keys
|
|
176
276
|
|
|
177
277
|
```bash
|
|
178
278
|
headband keys list [--json]
|
|
179
|
-
headband keys create <name> --type <admin|search> [--project <id>]
|
|
180
|
-
[--permissions <a,b,...>] [--expires <30d|ISO date>] [--json]
|
|
279
|
+
headband keys create <name> --type <admin|search> [--project <id>] [--branch <name>]
|
|
280
|
+
[--instances <id,id>] [--permissions <a,b,...>] [--expires <30d|ISO date>] [--json]
|
|
181
281
|
headband keys revoke <id> [--yes] [--json]
|
|
182
282
|
```
|
|
183
283
|
|
|
284
|
+
A key belongs to one branch and reaches only that branch's indexes, for an app
|
|
285
|
+
such as a preview deployment. `keys create` picks the branch in this order:
|
|
286
|
+
|
|
287
|
+
1. `--branch <name>`, in the project from `--project`, else the checkout's. A
|
|
288
|
+
branch id (`--branch br_...`) needs no project.
|
|
289
|
+
2. The checked-out branch, unless `--project` names another project.
|
|
290
|
+
3. The primary branch of `--project`.
|
|
291
|
+
|
|
292
|
+
With none of these, `--type admin` mints an org key, which reaches every branch
|
|
293
|
+
of every project, and `--type search` is refused (exit `2`, code
|
|
294
|
+
`branch_required`), because search keys ship in browsers. A key on the primary
|
|
295
|
+
branch is a live key (`hb_adm_live_`, `hb_src_live_`); a key on any other
|
|
296
|
+
branch is a test key (`hb_adm_test_`, `hb_src_test_`). `keys create` and
|
|
297
|
+
`keys list` show each key's branch and environment.
|
|
298
|
+
|
|
184
299
|
`keys create` is the only command that prints a raw API key. Store it when it is
|
|
185
300
|
shown: Headband returns raw keys only once.
|
|
186
301
|
|
|
302
|
+
`--instances <id,id>` also limits the key to those dedicated engines. It takes
|
|
303
|
+
one comma-separated list of 1 to 50 ids (from `headband instances list`);
|
|
304
|
+
repeating the flag does not add engines, only the last one counts. An org key then reaches only them, on every
|
|
305
|
+
branch. A branch key reaches its branch's engines among them, and every id must
|
|
306
|
+
sit on that branch. Any other index answers as if it did not exist, and
|
|
307
|
+
endpoints that are not about those engines refuse the key. The list is fixed:
|
|
308
|
+
to change it, create a new key and revoke the old one. `keys list` and
|
|
309
|
+
`keys create` show each key's engines: `all` when it is not limited, the engine
|
|
310
|
+
ids when it is, `none` when its list reaches nothing, and `unknown` when the
|
|
311
|
+
API did not report them (never shown as `all`).
|
|
312
|
+
|
|
187
313
|
`--permissions` limits the new key to the permissions you list. You can grant
|
|
188
314
|
only permissions your own key holds, and only ones that fit the key type:
|
|
189
315
|
|
|
@@ -194,8 +320,8 @@ only permissions your own key holds, and only ones that fit the key type:
|
|
|
194
320
|
|
|
195
321
|
Anything else is refused with exit `4` and the names it refused. Without
|
|
196
322
|
`--permissions`, the key gets the type's defaults, cut down to what your key
|
|
197
|
-
holds. `keys.manage`, `projects.manage
|
|
198
|
-
grantable here; only `headband login` mints them.
|
|
323
|
+
holds. `keys.manage`, `projects.manage`, `branches.manage` and
|
|
324
|
+
`schema.manage` are never grantable here; only `headband login` mints them.
|
|
199
325
|
|
|
200
326
|
`--expires` sets when the key stops working: a duration from now (`12h`,
|
|
201
327
|
`30d`, `2w`, `1y`) or an ISO date (`2027-01-31`). It must be in the future and
|
|
@@ -216,8 +342,18 @@ headband indexes settings set <uid> --file <settings.json>
|
|
|
216
342
|
headband indexes stats <uid>
|
|
217
343
|
```
|
|
218
344
|
|
|
345
|
+
`index` is an alias of `indexes` for every command here: `headband index list`
|
|
346
|
+
and `headband index switch-engine <uid>` work the same.
|
|
347
|
+
|
|
219
348
|
`settings get`, `settings set` and `stats` always print JSON.
|
|
220
349
|
|
|
350
|
+
`indexes create` prints the project and branch the index went to (CLI 0.6.0).
|
|
351
|
+
With the key from `headband login` and no `--project`, `--branch` or
|
|
352
|
+
checkout, that is the org's Default project, main branch.
|
|
353
|
+
|
|
354
|
+
Deleting an index that a `schema apply` reindex moved to `<uid>__v2` also
|
|
355
|
+
removes that build's own index row, so nothing is left behind.
|
|
356
|
+
|
|
221
357
|
`--instance <id>` creates the index on one of your dedicated engines (ids from
|
|
222
358
|
`headband instances list`). The index takes that engine's type, so `--engine` is
|
|
223
359
|
optional; if you pass it, it must match.
|
|
@@ -226,18 +362,43 @@ On the engine, the index has a longer name (`engineIndexUid`, such as
|
|
|
226
362
|
`hbo_<org>_products`). `indexes create` and `indexes list` show it. Use that name,
|
|
227
363
|
not the uid, with the engine's own client on the instance endpoint.
|
|
228
364
|
|
|
365
|
+
#### Change an index's engine
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
headband indexes switch-engine <uid> --engine <engine> [--instance <id>] [--accept-drops] [--declare-key] [--dry-run] [--wait] [--json]
|
|
369
|
+
headband indexes switch-engine <uid> --status [--json]
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Moves the index to another engine. The
|
|
373
|
+
uid, keys and URLs stay the same, and searches keep working until the switch.
|
|
374
|
+
Writes are held for about a minute at the end (they answer 503 with `Retry-After`).
|
|
375
|
+
|
|
376
|
+
- `--dry-run` shows how many documents would be copied, where the new index goes,
|
|
377
|
+
and which settings would not carry over. It changes nothing.
|
|
378
|
+
- A setting the new engine cannot apply stops the switch with exit code 6 and a
|
|
379
|
+
list of what would not carry over, with a reason for each. `--accept-drops`
|
|
380
|
+
switches anyway.
|
|
381
|
+
- `--wait` follows the switch until it ends and prints each phase. Without it the
|
|
382
|
+
command returns once the switch is queued; `--status` shows how it went.
|
|
383
|
+
- `--declare-key` is for a Visor index over 1000 documents whose primary key is
|
|
384
|
+
not filterable and sortable: the key is declared while the index is read, which
|
|
385
|
+
rebuilds it.
|
|
386
|
+
|
|
229
387
|
### Documents
|
|
230
388
|
|
|
231
389
|
```bash
|
|
232
390
|
headband docs push <file.json|file.jsonl> --index <uid> [--json]
|
|
233
391
|
headband docs export --index <uid> [--out <file>] [--format <jsonl|json>] [--batch <n>] [--json]
|
|
234
|
-
headband docs delete <docId
|
|
392
|
+
headband docs delete <docId>... --index <uid> [--json]
|
|
235
393
|
```
|
|
236
394
|
|
|
395
|
+
`docs delete` removes one or more documents by id, in one call to
|
|
396
|
+
`DELETE /v1/documents?index=<uid>`.
|
|
397
|
+
|
|
237
398
|
`docs push` accepts a JSON array or a JSONL file and sends documents to
|
|
238
399
|
`/v1/bulk` in batches of 1000.
|
|
239
400
|
|
|
240
|
-
`docs export`
|
|
401
|
+
`docs export` walks `GET /v1/documents` by cursor and writes every document in
|
|
241
402
|
the index, one page in memory at a time:
|
|
242
403
|
|
|
243
404
|
```bash
|
|
@@ -251,37 +412,62 @@ headband docs export --index products | head -5
|
|
|
251
412
|
- `--format` is `jsonl` (one document per line, the default) or `json` (one
|
|
252
413
|
array). With `--out`, a `.json` file name picks `json`.
|
|
253
414
|
- `--out` writes to `<file>.partial` and renames it when the export finishes,
|
|
254
|
-
so a failed export never leaves a truncated file under the real name.
|
|
255
|
-
|
|
415
|
+
so a failed export never leaves a truncated file under the real name. If the
|
|
416
|
+
export stops on an error after some documents were written (CLI 0.6.0), they
|
|
417
|
+
stay in `<file>.partial`, as a whole JSONL file or a closed JSON array, and
|
|
418
|
+
stderr says how many. `--json` with `--out` prints a summary:
|
|
419
|
+
`{index, out, format, exported}`.
|
|
256
420
|
- `--batch` is the page size. The default is 1000, or 250 for an index on
|
|
257
|
-
Typesense, which refuses larger pages.
|
|
258
|
-
-
|
|
259
|
-
|
|
421
|
+
Typesense, which refuses larger pages. The server may answer fewer a page.
|
|
422
|
+
- The cursor reaches every document on every engine: Visor past its first
|
|
423
|
+
1000 and Elasticsearch past its first 10,000. A server without cursors is
|
|
424
|
+
paged by offset, as before CLI 0.5.0.
|
|
425
|
+
- A Visor index created before 2026-10-21 with more than 1000 documents may
|
|
426
|
+
not have its primary key declared filterable and sortable. The server then
|
|
427
|
+
refuses the walk (exit `2`, code `feature_not_supported`), and the CLI prints
|
|
428
|
+
the fix: `headband indexes settings get <uid> > settings.json`, add the key
|
|
429
|
+
to the lists it names, `headband indexes settings set <uid> --file
|
|
430
|
+
settings.json` (Visor rebuilds the index), then export again. New Visor
|
|
431
|
+
indexes declare the key on their own.
|
|
260
432
|
- Export needs an admin key with `documents.read`.
|
|
261
433
|
|
|
262
434
|
### Search
|
|
263
435
|
|
|
264
436
|
```bash
|
|
265
|
-
headband search <index> <query> [--limit N] [--filter <expr>] [--json]
|
|
437
|
+
headband search <index> [<query>] [--limit N] [--filter <expr>] [--mode <instant|retrieval>] [--json]
|
|
266
438
|
```
|
|
267
439
|
|
|
440
|
+
Without a query, or with `""`, search browses the index (CLI 0.6.0): every
|
|
441
|
+
document matches, and `--filter` and `--limit` pick what comes back.
|
|
442
|
+
|
|
443
|
+
`--mode` picks Visor's ranking: `instant` (the default, typo-tolerant,
|
|
444
|
+
as-you-type) or `retrieval` (BM25F, for longer queries). Indexes on the other
|
|
445
|
+
engines ignore it.
|
|
446
|
+
|
|
268
447
|
Plain output shows a compact table. `--json` prints the raw API response.
|
|
269
448
|
|
|
270
449
|
### Tasks
|
|
271
450
|
|
|
272
451
|
```bash
|
|
273
|
-
headband tasks list [--json]
|
|
274
|
-
headband tasks get <id>
|
|
452
|
+
headband tasks list [--index <uid>] [--limit <n>] [--json]
|
|
453
|
+
headband tasks get <id> [--index <uid>] [--json]
|
|
275
454
|
```
|
|
276
455
|
|
|
277
456
|
`tasks list` prints a table, or the raw response with `--json`. `tasks get`
|
|
278
457
|
prints JSON.
|
|
279
458
|
|
|
459
|
+
Tasks live on the engine that holds the index. The key `headband login`
|
|
460
|
+
creates is org-wide, so it must name the index with `--index`. So must a key
|
|
461
|
+
of a branch other than production. A project key of the production branch may
|
|
462
|
+
leave it out.
|
|
463
|
+
|
|
280
464
|
### Engines
|
|
281
465
|
|
|
282
|
-
Dedicated engines are private Meilisearch, Elasticsearch
|
|
283
|
-
your organization, reachable with each engine's own client
|
|
284
|
-
gradually. Until your organization has access,
|
|
466
|
+
Dedicated engines are private Meilisearch, Elasticsearch, Typesense or Visor
|
|
467
|
+
servers for your organization, reachable with each engine's own client (Visor over
|
|
468
|
+
its HTTP API). They are rolling out gradually. Until your organization has access,
|
|
469
|
+
these commands say so. Visor is offered in a region where a dedicated engine host
|
|
470
|
+
runs it; elsewhere `instances create visor` answers `engine_not_available`.
|
|
285
471
|
|
|
286
472
|
```bash
|
|
287
473
|
headband instances list [--json]
|
|
@@ -308,23 +494,29 @@ woke, the outcome and how long it took to be ready. `--limit` takes up to 200;
|
|
|
308
494
|
the default is 50.
|
|
309
495
|
|
|
310
496
|
`connect` prints the endpoint and a snippet for the engine's own client.
|
|
311
|
-
Authenticate with an organization-wide key. The snippets create an index and add
|
|
497
|
+
Authenticate with an organization-wide key, a branch key of the engine's branch, or a key limited to that engine. The snippets create an index and add
|
|
312
498
|
a document, so run them with an admin key (`headband keys create <name> --type
|
|
313
|
-
admin`); a search key can only search.
|
|
314
|
-
endpoints. Serverless engines are available for Meilisearch, and for Typesense
|
|
499
|
+
admin`); a search key can only search. A key of another branch does not work on the engine. Serverless engines are available for Meilisearch, and for Typesense
|
|
315
500
|
in early access; Elasticsearch runs always on. If a command says the key cannot
|
|
316
501
|
manage engines, run `headband login` again.
|
|
317
502
|
|
|
318
503
|
### Schema migrations
|
|
319
504
|
|
|
320
505
|
For projects that define their indexes in `headband.config.ts` with
|
|
321
|
-
`@headband/sync`. These commands read that file from the current directory
|
|
506
|
+
`@headband/sync`. These commands read that file from the current directory:
|
|
507
|
+
its default export is `defineConfig({ url, projectId?, indexes })`, and `apply`
|
|
508
|
+
also needs a named `adapter` export, such as `drizzleAdapter(...)` from
|
|
509
|
+
`@headband/drizzle`, in the same file. When the adapter is exported, the
|
|
510
|
+
config is checked against your tables first, and every misspelled table,
|
|
511
|
+
field, relation or settings attribute is listed with its index.
|
|
512
|
+
`headband schema <command> --help` describes each command.
|
|
322
513
|
|
|
323
514
|
```bash
|
|
324
515
|
headband schema diff [--allow-destructive] [--json]
|
|
325
516
|
headband schema generate [--slug <slug>] [--dir <dir>] [--allow-destructive] [--json]
|
|
326
|
-
headband schema apply [--allow-destructive] [--dir <dir>] [--yes] [--json]
|
|
517
|
+
headband schema apply [--project <id>] [--allow-destructive] [--dir <dir>] [--yes] [--json]
|
|
327
518
|
headband schema status [--dir <dir>] [--json]
|
|
519
|
+
headband schema forget <index> [--yes] [--json]
|
|
328
520
|
```
|
|
329
521
|
|
|
330
522
|
- `diff` compares the config with the schema state on the server. Like
|
|
@@ -333,15 +525,33 @@ headband schema status [--dir <dir>] [--json]
|
|
|
333
525
|
- `generate` writes a migration file for the pending changes into `--dir`
|
|
334
526
|
(default `headband/migrations`). `--json` prints `{path}`, with `null` when
|
|
335
527
|
there was nothing to generate.
|
|
336
|
-
- `apply` runs the migration files not yet
|
|
337
|
-
|
|
528
|
+
- `apply` runs the migration files the server does not have yet, in order. A
|
|
529
|
+
new index is created and backfilled from every source row (rows
|
|
530
|
+
`shouldIndex` rejects are left out). A schema change builds a new index
|
|
531
|
+
beside the live one, backfills it and switches the name over. Rows your app
|
|
532
|
+
writes during the backfill still go to the live index, so `apply` then
|
|
533
|
+
compares every document of the new index with its source row and writes the
|
|
534
|
+
ones that differ, once before the switch and once after it (CLI 0.6.1).
|
|
338
535
|
Removing an index needs `--allow-destructive`. `--json` prints
|
|
339
|
-
`{appliedFiles, appliedOps}`.
|
|
340
|
-
- `
|
|
536
|
+
`{appliedFiles, appliedOps, projectId}`.
|
|
537
|
+
- `apply` recognises migrations the server already has from its schema state,
|
|
538
|
+
so a fresh checkout or CI applies without `headband/migrations/.applied.json`.
|
|
539
|
+
That file is only a local cache: commit it or ignore it.
|
|
540
|
+
- `status` shows each index's applied version and any drift, including
|
|
541
|
+
`index missing` for an index deleted outside `apply`. `--json` prints
|
|
341
542
|
`{indexes, localApplied, localHead}`.
|
|
543
|
+
- `forget` removes the recorded schema state of an index deleted outside
|
|
544
|
+
`apply`, so the next `generate` plans it as new. `headband indexes delete`
|
|
545
|
+
clears it for you.
|
|
342
546
|
|
|
343
547
|
Schema commands need a key with `schema.manage`, which only `headband login`
|
|
344
|
-
mints.
|
|
548
|
+
mints. That key is org-wide: new indexes go to the project named by
|
|
549
|
+
`--project <id>` or the config's `projectId`, else to your org's Default
|
|
550
|
+
project. Your app's own project admin key reaches only its project's indexes,
|
|
551
|
+
so name that project.
|
|
552
|
+
|
|
553
|
+
The URL comes from `--url`, else `HEADBAND_URL`, else the config's `url`, else
|
|
554
|
+
your login's.
|
|
345
555
|
|
|
346
556
|
### Sync
|
|
347
557
|
|
|
@@ -382,35 +592,62 @@ Headband's setup SQL on your machine as the tables' owner, in one transaction;
|
|
|
382
592
|
the owner URL never leaves your machine. Run `headband sync source --help` and
|
|
383
593
|
`headband sync index --help` for the details.
|
|
384
594
|
|
|
595
|
+
Managed Sync is in early access: until your organization has it, these
|
|
596
|
+
commands say "Sync is not enabled for this organization yet."
|
|
597
|
+
|
|
385
598
|
**Self-hosted Sync** runs a worker in your own infrastructure. Your app writes
|
|
386
|
-
changes to an outbox table, and the worker pushes them to Headband.
|
|
599
|
+
changes to an outbox table, and the worker pushes them to Headband. The
|
|
600
|
+
`@headband/drizzle` README walks through the whole setup.
|
|
387
601
|
|
|
388
602
|
```bash
|
|
389
|
-
headband sync init [--table <name>] [--write] [--json]
|
|
390
|
-
headband sync worker [--loop] [--budget-ms <ms>] [--batch-size <n>] [--reconcile-every <n>] [--json]
|
|
603
|
+
headband sync init [--table <name>] [--write] [--apply] [--json]
|
|
604
|
+
headband sync worker [--loop] [--budget-ms <ms>] [--batch-size <n>] [--reconcile-every <n>] [--project <id>] [--json]
|
|
391
605
|
headband sync worker --scaffold docker [--force] [--json]
|
|
392
|
-
headband sync reconcile [--fix] [--json]
|
|
606
|
+
headband sync reconcile [--content] [--fix] [--json]
|
|
393
607
|
headband sync retry-dead-letters [--json]
|
|
608
|
+
headband sync status [--json]
|
|
394
609
|
```
|
|
395
610
|
|
|
396
611
|
- `init` prints the outbox table's DDL; `--write` also saves it to
|
|
397
|
-
`headband/outbox.sql
|
|
612
|
+
`headband/outbox.sql`, and `--apply` runs it against `DATABASE_URL` (it is
|
|
613
|
+
`CREATE TABLE IF NOT EXISTS`). `--json` prints `{ddl, path, applied}`.
|
|
398
614
|
- `worker` drains the outbox. Without `--budget-ms` it loops until stopped
|
|
399
|
-
(Ctrl+C or SIGTERM finish the current batch first)
|
|
400
|
-
|
|
615
|
+
(Ctrl+C or SIGTERM finish the current batch first), so `--loop` changes
|
|
616
|
+
nothing on its own. With `--budget-ms` it stops after that long, or earlier
|
|
617
|
+
once the outbox is empty; add `--loop` to keep waiting for changes until the
|
|
618
|
+
budget ends. `--json` then prints the run report. Each batch that drained
|
|
619
|
+
rows prints one line to stderr (CLI 0.6.0). In a loop,
|
|
401
620
|
`--json` sends progress to stderr and prints the report when the worker
|
|
402
|
-
stops.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
`
|
|
621
|
+
stops. A row that fails is printed with its model, ids and error; a batch
|
|
622
|
+
that fails is retried row by row, so one bad row never fails the others.
|
|
623
|
+
Rows that keep failing move to the dead letters. A batch that fails because
|
|
624
|
+
Headband or the network is unavailable goes back to the queue without
|
|
625
|
+
counting an attempt, so an outage never fills the dead letters (CLI 0.6.1). Each invocation is recorded
|
|
626
|
+
as one run under the connector `orm-sync` (`GET /v1/connectors/runs`); with
|
|
627
|
+
the org-wide login key that needs `--project <id>` or the config's
|
|
628
|
+
`projectId`. `--reconcile-every <n>` runs `reconcile` after every n-th batch.
|
|
629
|
+
- `worker --scaffold docker` writes `Dockerfile.headband-sync`, which runs
|
|
630
|
+
`npx --yes @headband/cli@<version> sync worker --loop`, and prints a compose
|
|
631
|
+
service for it. The container needs `DATABASE_URL` and `HEADBAND_API_KEY`.
|
|
632
|
+
- `reconcile` compares the source rows that belong in each index (every row
|
|
633
|
+
`shouldIndex` keeps) with the documents in it, and reports how many are
|
|
634
|
+
missing and how many are extra (counts, not ids). `--content` also compares
|
|
635
|
+
every document with its freshly resolved source row and counts the stale
|
|
636
|
+
ones; it reads the whole index, so it takes about as long as a backfill (CLI
|
|
637
|
+
0.6.1). `--fix` pushes the missing (and with `--content` the stale) and
|
|
638
|
+
deletes the extra. It exits
|
|
639
|
+
`1` when drift is left. `--json` prints `{indexes, drift}`. The index's ids
|
|
640
|
+
are read by cursor, so a Visor index past 1000 documents is compared id by
|
|
641
|
+
id too.
|
|
408
642
|
- `retry-dead-letters` requeues rows that failed too many times. `--json`
|
|
409
643
|
prints `{requeued}`.
|
|
644
|
+
- `status`, beside a `headband.config.ts`, shows the outbox's pending rows,
|
|
645
|
+
dead letters and oldest pending row. Elsewhere it shows managed Sync.
|
|
410
646
|
|
|
411
|
-
The worker
|
|
412
|
-
`DATABASE_URL`, falling back to `HEADBAND_DATABASE_URL`. The
|
|
413
|
-
`reconcile` need `headband.config.ts` to export an `adapter`.
|
|
647
|
+
The worker, `retry-dead-letters`, `status` and `init --apply` read the outbox
|
|
648
|
+
database URL from `DATABASE_URL`, falling back to `HEADBAND_DATABASE_URL`. The
|
|
649
|
+
worker and `reconcile` need `headband.config.ts` to export an `adapter`. A
|
|
650
|
+
missing outbox table is reported by name, with how to create it.
|
|
414
651
|
|
|
415
652
|
## What `init` creates
|
|
416
653
|
|