@seliseblocks/cli-os 0.2.9 → 0.2.11

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/AI_USAGE_GUIDE.md CHANGED
@@ -1,560 +1,560 @@
1
- # Blocks CLI Guide for AI Agents
2
-
3
- This guide is for AI agents using the published `@seliseblocks/cli-os` npm package. The installed binary is `blocks`.
4
-
5
- Use `blocks` as the control plane. If a capability exists in the CLI, call the CLI from the terminal instead of calling Blocks cloud APIs directly from ad hoc scripts or generated application code.
6
-
7
- ## Install
8
-
9
- Install the package in the environment where the agent will operate:
10
-
11
- ```bash
12
- npm install -g @seliseblocks/cli-os
13
- ```
14
-
15
- Verify the binary:
16
-
17
- ```bash
18
- blocks --version
19
- blocks --help
20
- ```
21
-
22
- For local package development only, contributors may run `node bin/run.js ...` from the source repository. AI agents consuming the npm package should use `blocks ...`.
23
-
24
- ## Global Options
25
-
26
- Namespaced commands accept either spaces or colons, e.g. `blocks data schema list` and
27
- `blocks data:schema:list` are equivalent. Global options available on every command:
28
-
29
- - `--json` - print machine-readable JSON where supported.
30
- - `--api-url <url>` - override the Blocks API URL for this command.
31
- - `--account <name>` - use a named account profile; default is implicit.
32
- - `--project <tenantId>` - use a project tenant for project-scoped commands.
33
- - `--dry-run` / `--yes` - see Operating Rules below.
34
-
35
- Use `blocks --help` (no subcommand) as command ground truth for what exists. Do not probe an individual subcommand with `<command> --help` to check its flags - most subcommands don't recognize `--help` as special and just run their real logic with it as a no-op argument (e.g. `login --help` performs an actual login attempt; `new web <name> --help` runs real arg validation). If you need a subcommand's full flag list, read this guide's section for it or infer from `--dry-run`/error output instead.
36
-
37
- ## Operating Rules
38
-
39
- - Use `blocks ...` for all supported Blocks OS, IAM, Data, Release, and scaffold operations.
40
- - Prefer `--json` for automation and parsing.
41
- - Use `--dry-run` before any mutating command.
42
- - Do not run real mutating cloud commands unless the user explicitly approved the exact action.
43
- - Never print, commit, scaffold, or document access tokens, refresh tokens, cookies, JWTs, or other secrets.
44
- - Treat any secret pasted into chat or logs as exposed and rotate it before production use.
45
- - Generated apps must not contain CLI tokens.
46
- - If a CLI command returns an error, fix or report the CLI path. Do not bypass the CLI with a one-off API request when the command exists.
47
-
48
- ## Login
49
-
50
- Device-code login uses the packaged OS client id. It prints a verification URL and user code, opens the browser to the verification page when possible, then polls until approved:
51
-
52
- ```bash
53
- blocks login
54
- ```
55
-
56
- Check current auth state:
57
-
58
- ```bash
59
- blocks auth status --json
60
- ```
61
-
62
- If local auth state is stale or corrupted (Windows profile change, machine migration, Keychain reset), clear local auth state and log in again:
63
-
64
- ```bash
65
- blocks auth remove <account>
66
- blocks login
67
- ```
68
-
69
- Use `blocks logout` to revoke the current refresh token when possible and remove local session data. Use `blocks auth refresh --json` to force account token refresh, and `blocks auth refresh --project --json` after a project session already exists.
70
-
71
- Run health checks without mutation:
72
-
73
- ```bash
74
- blocks doctor --json
75
- ```
76
-
77
- ## Project Workflow
78
-
79
- List projects:
80
-
81
- ```bash
82
- blocks projects list --json
83
- ```
84
-
85
- `projects create` is currently disabled in this build (commented out pending a product decision) - do not tell users it's available, and do not try to work around its absence with a raw API call. Projects must already exist (created from the Blocks portal) before selecting one below.
86
-
87
- Select a project:
88
-
89
- ```bash
90
- blocks use <projectTenantId>
91
- ```
92
-
93
- Read the selected project:
94
-
95
- ```bash
96
- blocks projects get --json
97
- ```
98
-
99
- If an impersonated project token has expired or failed and re-running the command doesn't
100
- recover, clear the selection and reselect to force re-impersonation:
101
-
102
- ```bash
103
- blocks deselect
104
- blocks use <projectTenantId>
105
- ```
106
-
107
- ## Scaffold a Web App
108
-
109
- Generate a React/Vite Blocks app. All of `--x-blocks-key`, `--app-domain`, and `--client-id` are optional now - they're resolved from the selected project when omitted:
110
-
111
- ```bash
112
- blocks use <projectTenantId> # if not already selected
113
- blocks new web <appName>
114
- ```
115
-
116
- This is interactive when a value isn't already known: if the project has more than one registered domain you're prompted to choose; the OIDC client is offered as a pick-list of the project's existing clients, plus "create a new one now" (prompts only for display name + redirect URI, defaulting to `https://<appDomain>/login/callback`) or "skip, register later." Do not fabricate a client id or domain value yourself.
117
-
118
- **An AI agent running this non-interactively will hang on these prompts** - there's no stdin to answer "Choose 1-3:" from an automated process. Before running `new web`, gather the values yourself and pass them explicitly:
119
-
120
- ```bash
121
- blocks projects get --json # see the project's domain(s) under project.applications
122
- blocks auth oidc-clients list --json # see existing OIDC clients, if any
123
- ```
124
-
125
- Then run with explicit flags so no prompt is reached:
126
-
127
- ```bash
128
- blocks new web <appName> --x-blocks-key <projectTenantId> --app-domain <appDomainOrUrl> --client-id <publicOidcClientId>
129
- ```
130
-
131
- `new web` also accepts `--blocks-api-url <url>` and `--oidc-url <url>`, same as `sdk client`
132
- below. When `--blocks-api-url` is omitted, the scaffold derives it from the app domain as
133
- `https://blocksapi.<registrable-domain>`; for example `https://dqrsf.slsblx.com` becomes
134
- `https://blocksapi.slsblx.com`. Pass `--blocks-api-url` only when targeting a non-default Blocks gateway. `--oidc-url` defaults to `https://iam.seliseblocks.com`.
135
-
136
- Validate the scaffold:
137
-
138
- ```bash
139
- cd <appName>
140
- npm install
141
- npm run build
142
- ```
143
-
144
- Do not pass CLI auth state to the scaffolded app. Browser apps must use a public OIDC client and the SDK hosted IdP flow: `blocksClient.auth.idp.redirectToProvider()` on login click and `blocksClient.auth.idp.callback()` on `/login/callback`.
145
-
146
- `--app-domain` is the app's real Blocks domain/origin, for example `https://dbpdba.seliseblocks.com`. The generated `.env` keeps that full value as `VITE_BLOCKS_APP_DOMAIN` and derives the local dev host without a scheme as `VITE_BLOCKS_DEV_HOST=dbpdba.seliseblocks.com`.
147
-
148
- For local browser login on the real host domain:
149
-
150
- 1. Add `127.0.0.1 <VITE_BLOCKS_DEV_HOST>` to the hosts file.
151
- 2. Run `npm install`.
152
- 3. Run `npm run cert`.
153
- 4. Run `npm run dev`.
154
- 5. Open `https://<VITE_BLOCKS_DEV_HOST>:5173`, not plain `http://`.
155
-
156
- The generated cert script uses the `selfsigned` Node dependency, so it works from normal PowerShell after `npm install`; do not tell Windows users to switch to Git Bash just for OpenSSL. If hosted login or secure cookies fail locally, confirm the app is opened with the HTTPS dev URL from `VITE_BLOCKS_DEV_HOST`.
157
-
158
- ## SDK Client (read-only)
159
-
160
- `sdk client` answers "I want to use the Blocks SDK - show me the client." It resolves this project's `@seliseblocks/client` config (same values `new web` scaffolds an app with) and prints a ready-to-paste `createBlocksClient(...)` snippet - **it never writes a file or mutates anything**. To scaffold a full app instead, use `new web` above.
161
-
162
- ```bash
163
- blocks sdk client --x-blocks-key <projectTenantId> --app-domain <appDomainOrUrl> --client-id <publicOidcClientId> --blocks-api-url https://api.seliseblocks.com
164
- ```
165
-
166
- Unlike `new web`, `sdk client` keeps `--blocks-api-url` defaulted to `https://api.seliseblocks.com`; only pass it explicitly if your project uses a different gateway URL. Passing both `--app-domain` and `--client-id` skips the project lookup entirely, so it needs no CLI login at all - useful for a quick, non-interactive check. Omit either one and it resolves from the selected project instead (auto-picks when there's exactly one match, otherwise lists the options and asks you to pass the flag explicitly - it does not prompt or create anything, since this command is read-only). Use `--json` for the resolved values instead of the snippet.
167
-
168
- ## Skills
169
-
170
- `skill list [--json]` / `skill show <name> [--json]` / `skill add <name> [--dir <path>]` read this package's bundled copy of `blocks-skills/*/SKILL.md` - local-only, no cloud calls. `skill add` copies a skill's **entire directory** (`SKILL.md` plus any supporting files, e.g. `flows/*.md`) into `<dir>/<name>/` (default `./blocks-skills`) in the current directory, for pulling a single skill into a project outside this monorepo. `skill list`'s human-readable output (and the "unknown skill" error from `show`/`add`) both point at the full public skill catalog, in case the locally bundled set is out of date. As with any skill file, verify command names against this guide or `blocks --help` before running them - skills are conversational context, not command ground truth.
171
-
172
- ## IAM, MFA, and Auth Admin
173
-
174
- `iam me` reads the CLI operator's own account identity (bootstrapping, not a project resource):
175
-
176
- ```bash
177
- blocks iam me --json
178
- ```
179
-
180
- Every other command below is project-scoped: it requires a project already selected (`blocks use <tenantId>`) and always calls IAM through an impersonated project token - never the account token, and never something you construct yourself. If no project is selected, the command fails with `project_not_selected`; run `blocks use <tenantId>` first (see Agent Failure Handling).
181
-
182
- Command families (run `blocks --help` for the full flag reference on each):
183
-
184
- - `iam users *`, `iam email available` - list/get/create/update/activate/deactivate, access grant/revoke, existence and email-availability checks.
185
- - `iam roles *` - list/get/create/update, assign-permissions, assignable. `assign-permissions` accepts permission resource strings and resolves them to itemIds before sending IAM's id-based mutation.
186
- - `iam permissions *` - list/get/create/update, by-severity.
187
- - `iam resources *` - resource groups and feature flags (read-only).
188
- - `iam organizations *` - list/get/create/update, `my`, and organization config get/save.
189
- - `iam signup-settings *` - get/save tenant signup policy.
190
- - `mfa config *`, `mfa totp *`, `mfa generate`/`resend`/`verify`, `mfa method set`, `mfa disable`, `mfa backup-codes *` - tenant MFA policy plus enrollment/verification/backup-code flows.
191
- - `mfa totp enable --mfa-type <n>` - composed TOTP enrollment: `totp setup` → prints the QR/secret → `totp verify-setup` → `method set` → `backup-codes generate`, one confirmation. Prefer this over running the individual steps. `--mfa-type` is required and not defaulted - the tenant-specific integer meaning "TOTP" isn't documented anywhere in this CLI; don't guess it, ask the user or check `mfa config get`. **Prompts interactively for the verification code unless `--code <c>` is given** - an agent running this non-interactively must supply `--code` (from wherever the user's authenticator app output is captured) or it will hang waiting on stdin. Deliberately excludes `mfa config save` (a separate tenant-wide admin policy, not part of one user's enrollment).
192
- - `auth idp *` - identity provider (SSO/OIDC) configuration: list/get/create/update/delete/status.
193
- - `auth config *` - AuthController tenant config (token lifetimes, lockout policy, etc.).
194
- - `auth client-credentials *` - machine-to-machine client credentials: list/save/delete.
195
- - `auth oidc-clients *` - OIDC client app registrations: list/get/save (upsert)/delete/rotate-secret.
196
-
197
- Rules:
198
-
199
- - Use `--dry-run` before any mutating command in these families, the same as Data/Localization/Release, then `--yes` only after explicit approval.
200
- - Rich payloads (identity provider config, OIDC client config, user/role/permission create-update bodies, etc.) accept `--body '<json>'` or `--file <path.json>` on top of the documented convenience flags - use whichever is easier for the exact fields you need to set.
201
- - `auth idp create`/`update`, `auth client-credentials save`, and `auth oidc-clients save`/`rotate-secret` can return a `client_secret` shown only once. Never print, log, commit, or otherwise persist it outside what the user explicitly asked to store; treat that response the same as any other CLI-managed secret.
202
- - Do not add IAM/MFA/Auth admin behavior outside these supported CLI commands unless the CLI package is explicitly extended and tested.
203
-
204
- ## Data
205
-
206
- Check the data-source configuration first. Most projects run on Blocks-managed storage by default, so this is usually the only `data config *` command you need:
207
-
208
- ```bash
209
- blocks data config get --json
210
- ```
211
-
212
- Only create/update a data source configuration after explicit user approval - it points the project's Data Gateway at a different (external) database, which is a deliberate, rare action:
213
-
214
- ```bash
215
- blocks data config create --connection-string "<cs>" --database-name "<name>" --dry-run --json
216
- blocks data config create --connection-string "<cs>" --database-name "<name>" --yes --json
217
- blocks data config update --item-id <id> --connection-string "<cs>" --dry-run --json
218
- blocks data config update --item-id <id> --connection-string "<cs>" --yes --json
219
- ```
220
-
221
- Validate local files:
222
-
223
- ```bash
224
- blocks data validate --json
225
- ```
226
-
227
- List schemas:
228
-
229
- ```bash
230
- blocks data schema list --json
231
- ```
232
-
233
- Pull schemas:
234
-
235
- ```bash
236
- blocks data schema pull --json
237
- ```
238
-
239
- Push schemas only after dry-run and approval:
240
-
241
- ```bash
242
- blocks data schema push --dry-run --json
243
- blocks data schema push --yes --json
244
- ```
245
-
246
- Pull rules:
247
-
248
- ```bash
249
- blocks data rules pull --json
250
- ```
251
-
252
- Deploy rules only after dry-run and approval:
253
-
254
- ```bash
255
- blocks data rules deploy --dry-run --json
256
- blocks data rules deploy --yes --json
257
- ```
258
-
259
- Reload Data schema configuration only after approval:
260
-
261
- ```bash
262
- blocks data reload --dry-run --json
263
- blocks data reload --yes --json
264
- ```
265
-
266
- **Prefer `data sync` over running validate/push/deploy/reload separately.** It composes all four (validate → `schema push` → `rules deploy` → `data reload`) behind one confirmation, and it's the only way to guarantee the reload actually happens - nothing else calls it automatically, so schema/rule changes pushed without a following `data reload` can sit staged without going live:
267
-
268
- ```bash
269
- blocks data sync --dry-run --json
270
- blocks data sync --yes --json
271
- ```
272
-
273
- It validates first and hard-fails with no API calls made if schemas or the rules file don't parse/validate. It prints 3 separate step outputs (one per underlying command), not one combined JSON document - parse each block in sequence if you need machine-readable results from all three.
274
-
275
- ### Raw Data API
276
-
277
- `validate`/`schema list`/`schema pull`/`schema push`/`rules pull`/`rules deploy`/`reload` above cover the common file-oriented workflow. The rest of `/data/v4/*` is exposed directly, project-scoped with an impersonated project token only. Run `blocks --help` for the full flag reference on each; command families:
278
-
279
- - `data schema get`/`get-by-name`/`aggregation`/`change-logs`/`delete` - single-schema lookup by id or collection name, access-level aggregation summary, unadapted change logs (cleared by `data reload`), and irreversible delete. `schema get` also prints the schema's exact GraphQL operation names in non-`--json` output; do not guess pluralized names -- generated names are naive string concatenation (`Company` -> `getCompanys`, not `getCompanies`), read them from `querySchema`/`mutationSchemas` instead.
280
- - `data schema info list`/`save`/`update` + `data schema fields` - a two-step alternative to `schema push` (create/update schema metadata, then add/update field definitions separately). Prefer the file-oriented `schema push` workflow for normal authoring; use these only for a targeted metadata or field-only change without touching the local schema JSON.
281
- - `data rules policy get`/`delete` - read or delete one data-access policy without a full `rules pull`/edit/`rules deploy` round-trip.
282
- - `data validation list`/`get`/`by-schema`/`by-schema-field`/`save`/`delete` - field-level validation rules. No file-oriented workflow exists for these (no local JSON file to pull/push). `save` is an upsert (omit `--item-id` to create, pass it to update) and requires a `validations` array passed via `--body`/`--file` - there's no scalar flag for it, e.g. `--body '{"validations":[{"type":1,"value":"^[0-9]+$","isActive":true}]}'`.
283
- - `data files *` - permission-aware storage object tree: upload/download, directory CRUD/move, cursor list/search, versions, copy/move/rename, trash/restore/purge, shared objects, and access policies/inheritance.
284
-
285
- Same rules as everywhere else: `--dry-run` before any mutating command, then `--yes` only after explicit approval.
286
-
287
- **`--file` means two different things depending on the command.** Everywhere else in this CLI (`--body '<json>'`/`--file <path.json>`), `--file` is a JSON payload file read by `jsonBodyFlag`. On the `data files *` upload commands (`upload-to-url`, `upload-to-local-storage`), `--file` is instead the local binary file to read and upload - there is no JSON payload involved. Don't conflate the two: passing a JSON path to `data files upload-to-local-storage --file` uploads the JSON text as the file's bytes, it does not set a request body.
288
-
289
- **Prefer the composed `data files upload` over the manual steps below.** For cloud storage it creates the file/version metadata and PUTs the bytes; for local storage it performs one multipart call. Either path creates the visible object directly—there is no DMS registration step:
290
-
291
- ```bash
292
- blocks data files upload --file ./invoice.pdf --access-modifier Public --dry-run --json
293
- blocks data files upload --file ./invoice.pdf --access-modifier Public --yes --json
294
- blocks data files upload --file ./invoice.pdf --local-storage --yes --json # local-storage-backed projects
295
- ```
296
-
297
- Manual cloud-storage upload, if you need the intermediate steps for some reason (two calls):
298
-
299
- ```bash
300
- blocks data files presigned-upload-url --name invoice.pdf --access-modifier Public --dry-run --json
301
- blocks data files presigned-upload-url --name invoice.pdf --access-modifier Public --yes --json
302
- # take the returned uploadUrl and fileId, then:
303
- blocks data files upload-to-url --url "<uploadUrl>" --file ./invoice.pdf --content-type application/pdf --dry-run --json
304
- blocks data files upload-to-url --url "<uploadUrl>" --file ./invoice.pdf --content-type application/pdf --yes --json
305
- ```
306
-
307
- Manual local-storage upload (one call):
308
-
309
- ```bash
310
- blocks data files upload-to-local-storage --file ./invoice.pdf --access-modifier Public --dry-run --json
311
- blocks data files upload-to-local-storage --file ./invoice.pdf --access-modifier Public --yes --json
312
- ```
313
-
314
- Browse the resulting object tree with cursor pagination. Deletion defaults to trash:
315
-
316
- ```bash
317
- blocks data files list --parent-id <directoryId> --limit 50 --json
318
- blocks data files search invoice --directory-id <directoryId> --json
319
- blocks data files delete <fileId> --dry-run --json
320
- blocks data files delete <fileId> --yes --json
321
- blocks data files trash --json
322
- blocks data files restore <fileId> --dry-run --json
323
- ```
324
-
325
- ## Localization
326
-
327
- Generate or update local i18n dictionaries as JSON, then let the CLI sync them to Blocks Localization. Do not ask humans to manually copy keys into the portal.
328
-
329
- Default file convention:
330
-
331
- ```text
332
- blocks/localization/<module>.<language>.json
333
- ```
334
-
335
- Example:
336
-
337
- ```json
338
- {
339
- "dashboard.title": "Dashboard",
340
- "products.empty": "No products found"
341
- }
342
- ```
343
-
344
- Nested JSON is accepted on input and flattened before validation:
345
-
346
- ```json
347
- {
348
- "dashboard": {
349
- "title": "Dashboard"
350
- }
351
- }
352
- ```
353
-
354
- Validate first:
355
-
356
- ```bash
357
- blocks localization validate --module common --language en --json
358
- ```
359
-
360
- Push only after dry-run and approval:
361
-
362
- ```bash
363
- blocks localization push --module common --language en --dry-run --json
364
- blocks localization push --module common --language en --yes --json
365
- ```
366
-
367
- Pull published cloud localization when local fallback files need to be refreshed:
368
-
369
- ```bash
370
- blocks localization pull --module common --language en --json
371
- ```
372
-
373
- Use Localization gateway v4 paths without `/api`: `/localization/v4/Module/Gets`, `/localization/v4/Module/Save`, `/localization/v4/Key/SaveKeys`, and `/localization/v4/Key/GetCloudUilmFile`.
374
-
375
- ### Raw Localization API
376
-
377
- `validate`/`push`/`pull` above cover the common i18n file workflow. Every other `/localization/v4/*` endpoint is also exposed directly, project-scoped with an impersonated project token only (never the account token). Run `blocks --help` for the full flag reference on each; command families:
378
-
379
- - `localization assistant translation-suggestion` - AI translation suggestion for a single string (`--source-text`, `--destination-language`, optional glossary/context flags).
380
- - `localization config get-webhook`/`save-webhook` - tenant webhook config for localization change notifications.
381
- - `localization glossary save`/`list`/`get`/`suggested`/`delete` - glossary term CRUD and AI-suggested glossary lookup.
382
- - `localization key save`/`list`/`get-by-names`/`get`/`delete`/`delete-keys` - key CRUD and search beyond the bulk `push`/`pull` flow.
383
- - `localization key get-timeline`/`get-localization-timeline`/`get-timeline-by-operation-id`/`rollback` - key/tenant change history and rollback.
384
- - `localization key get-uilm-file`/`generate-uilm-file`/`uilm-import`/`uilm-export`/`get-uilm-exported-files`/`get-language-file-generation-history` - UILM language-file generation and import/export jobs.
385
- - `localization key translate-all`/`translate-key`/`translate-keys` - trigger AI machine translation for a module or specific keys.
386
- - `localization key translate-and-export --module-id <id> [--wait]` - composed: `translate-all` → `generate-uilm-file` → `uilm-export`. Prefer this over running the three by hand. `--wait` polls translation progress first via a self-generated correlation id (translation is async and has no documented "done" field, so this is a best-effort heuristic - it prints the raw response every poll); without `--wait` it just fires all three back to back like running them manually in sequence.
387
- - `localization language save`/`list`/`list-for-tenant`/`delete`/`set-default` - tenant language catalog management.
388
- - `localization module save`/`list`/`list-for-tenant`/`tag-glossary` - module CRUD and glossary tagging.
389
-
390
- Same rules as everywhere else: `--dry-run` before any mutating command, then `--yes` only after explicit approval; rich payloads accept `--body '<json>'`/`--file <path.json>` on top of the documented convenience flags. `localization config save-webhook`'s `--secret` is redacted in `--dry-run` output only - treat the live response as a secret.
391
-
392
- ## Mail
393
-
394
- Project-scoped SMTP/inbound mail configuration, templates, and mailbox reads via `/os/v4/Mail/*`:
395
-
396
- ```bash
397
- blocks mail config list --json
398
- blocks mail config get <name> --json
399
- blocks mail config save --name <n> --host <h> --port <p> --enable-ssl \
400
- --sender-name <n> --sender-address <addr> --account-password <p> --dry-run --json
401
- blocks mail config save --configuration-id <id> ... --yes --json # update
402
- blocks mail config delete <configurationId> --dry-run --json
403
- blocks mail config duplicate <configurationId> --dry-run --json
404
-
405
- blocks mail template list --configuration-id <id> --json
406
- blocks mail template get <itemId> --json
407
- blocks mail template save --configuration-id <id> --name <n> --language <l> \
408
- --subject <s> --template-body <html> --dry-run --json
409
- blocks mail template delete <itemId> --dry-run --json
410
- blocks mail template clone <itemId> --name <n> --dry-run --json
411
-
412
- blocks mail mailbox list --configuration-id <id> --json
413
- blocks mail mailbox get <messageId> --json
414
- ```
415
-
416
- Treat `--account-password` as a secret; the CLI redacts it in `--dry-run` output but the live response is still yours to protect.
417
-
418
- Sending mail is a separate surface, `/logic/v4/Mail/Send` and `/logic/v4/Mail/SendToAny` (not `/os/v4`):
419
-
420
- ```bash
421
- blocks mail send --to a@example.com,b@example.com --purpose welcome --language en \
422
- --subject-data-context '{"firstName":"Ada"}' --dry-run --json
423
- blocks mail send --to a@example.com --purpose welcome --language en --yes --json
424
-
425
- blocks mail sendtoany --to a@example.com --purpose welcome --language en \
426
- --is-test-mail --dry-run --json
427
- ```
428
-
429
- `--project-key` defaults to the selected project's tenant id; pass it explicitly only to target a different one. `--attachments`/`--subject-data-context`/`--body-data-context` take raw JSON.
430
-
431
- ## Notification
432
-
433
- Project-scoped notification channel configuration via `/os/v4/Notification/*`:
434
-
435
- ```bash
436
- blocks notification list --json
437
- blocks notification get <itemId> --json
438
- blocks notification save --name <n> --channel <0|1> --type <0-3> --dry-run --json
439
- blocks notification save --name <n> --channel <0|1> --type <0-3> --update --yes --json
440
- blocks notification delete <itemId> --dry-run --json
441
- ```
442
-
443
- `--channel` and `--type` are raw numeric enum values from the Blocks OS API (`NotifierTypes`, `NotificationReceiverTypes`) — the API does not publish names for them.
444
-
445
- ## Notifier
446
-
447
- Real-time/offline notification sends and inbox reads via `/logic/v4/Notifier/*` — distinct from
448
- `notification` above, which manages channel *configuration*, not sending:
449
-
450
- ```bash
451
- blocks notifier notify --user-ids u1,u2 --response-key status --response-value ok --dry-run --json
452
- blocks notifier notify --roles admin --denormalized-payload '{"orderId":"123"}' \
453
- --save-denormalized-payload-as-object --yes --json
454
- blocks notifier notify --subscription-filters '[{"context":"orders","actionName":"created","value":"*"}]' --yes --json
455
-
456
- blocks notifier list --unread-only --page 1 --page-size 20 --json
457
- blocks notifier unread --user-id <id> --context orders --action-name created --json
458
- blocks notifier mark-read <notificationId> --dry-run --json
459
- blocks notifier mark-all-read --dry-run --json
460
- ```
461
-
462
- Target `notify` with at least one of `--user-ids`/`--roles`/`--subscription-filters`. `notifier unread`
463
- sends its filter as query parameters even though swagger documents that endpoint as GET with a JSON
464
- body, which the Fetch spec forbids — the CLI and SDK both flatten it into the query string instead.
465
-
466
- ## Secrets
467
-
468
- Generic tenant secret storage via `/os/v4/Secrets/*` (e.g. captcha provider config):
469
-
470
- ```bash
471
- blocks secrets get captcha --json
472
- blocks secrets save --secret-key captcha \
473
- --key-value-pairs '{"isEnable":"true","provider":"recaptcha","captchaKey":"...","captchaSecret":"..."}' \
474
- --dry-run --json
475
- blocks secrets save --secret-key captcha --item-id <itemId> --key-value-pairs '{...}' --yes --json
476
- ```
477
-
478
- `--key-value-pairs` is a flat JSON object of provider-specific fields — its shape depends entirely on
479
- `--secret-key` (there's no fixed schema across secrets). `save` is an upsert: omit `--item-id` to
480
- create, pass it to update. Fields that look like secrets/keys are redacted in `--dry-run` output only.
481
-
482
- ## Storage
483
-
484
- Project-scoped storage backend configuration via `/os/v4/Storage/*`:
485
-
486
- ```bash
487
- blocks storage config list --json
488
- blocks storage config get <name> --json
489
- blocks storage config save --name <n> --strategy <s> --secret-key <k> --access-key <k> --dry-run --json
490
- blocks storage config save --item-id <id> --update ... --yes --json # update
491
- blocks storage config delete <name> --dry-run --json
492
- ```
493
-
494
- `--secret-key`, `--access-key`, `--password`, and `--connection-string` are secrets; the CLI redacts them in `--dry-run` output only.
495
-
496
- ## Release
497
-
498
- `release deploy` needs no `--repo-id` - it resolves the repo linked to the selected project (`Project/GetAsset`) and that repo's connected branch (`Build/repo-details`) on its own, and refuses to deploy if the connected branch doesn't match the project's environment name. Trigger a deploy only after dry-run and approval:
499
-
500
- ```bash
501
- blocks release deploy --dry-run --json
502
- blocks release deploy --yes --json
503
- blocks release deploy --domain <customDomain> --yes --json # also sets the custom deployment domain first
504
- blocks release deploy --yes --wait --json # poll until the build finishes instead of returning the build id
505
- ```
506
-
507
- If no repo is linked yet, the command fails with `repo_not_linked` - that requires GitHub OAuth, so it can only be done from the Blocks portal; do not attempt to link a repo from the CLI.
508
-
509
- `--wait` polls `/release/v4/api/Build` (same data `release status` reads) every `--poll-interval` seconds (default 10) until a terminal-looking state is detected or `--timeout` elapses (default 900s). There's no documented status field/enum for this endpoint, so "terminal" is a best-effort text match (success/fail/complete/cancel/etc. anywhere in the response) - the raw JSON is printed every poll, so verify against that rather than trusting the heuristic blindly. Without `--wait`, `release deploy` returns immediately with just a build id, same as before.
510
-
511
- Read build status:
512
-
513
- ```bash
514
- blocks release status <buildId> --json
515
- blocks release builds get <buildId> --json
516
- ```
517
-
518
- List builds for a repository (repoId is optional now - omit it to resolve from the selected project's linked repo assets, auto-picked if there's exactly one, otherwise interactively prompted, which will hang a non-interactive agent - pass `--repo-id` explicitly if you don't already know there's exactly one):
519
-
520
- ```bash
521
- blocks release builds list --repo-id <repoId> --json
522
- ```
523
-
524
- ## Agent Failure Handling
525
-
526
- - `not_logged_in`: run `blocks login`, then `blocks projects list`, then `blocks use <tenantId>`.
527
- - `refresh_token_rejected`: run `blocks login`.
528
- - `refresh_network_error`: check the network and configured OIDC URL, then retry.
529
- - `auth_repair_required`: inspect `blocks auth status --json`; if local storage is unreadable or stale, run `blocks auth remove <account>`, then `blocks auth status --json` and `blocks login`.
530
- - `project_not_selected`: run `blocks projects list`, then `blocks use <projectTenantId>`.
531
- - `api_auth_failed`: run `blocks auth status --json`, then login again. If the failure is specifically a stale/expired impersonated project token rather than the account token, `blocks deselect` followed by `blocks use <tenantId>` re-impersonates without a full re-login.
532
- - `repo_not_linked` (from `release deploy`): no repo is linked to this project. This needs GitHub OAuth - tell the user to link it from the Blocks portal, do not retry from the CLI.
533
- - `repo_ambiguous` (from `release deploy`): multiple repos are linked and none is named for the current environment. Tell the user to check the project's repo links in the portal.
534
- - `repo_not_found` (from `release deploy`): the linked asset's repo id doesn't exist in blocks-release. Tell the user to check the project's repo link in the portal.
535
- - `branch_environment_mismatch` (from `release deploy`): the connected repo's branch doesn't match this environment's name. The message states the branch found and the environment required - do not retry; the repo's connected branch must be fixed first.
536
- - `build_wait_timeout` (from `release deploy --wait`): the build didn't reach a detected terminal state within `--timeout`. The deploy itself already succeeded (this only affects the wait) - check manually with `release status <buildId>` rather than assuming failure.
537
- - `translation_wait_timeout` (from `localization key translate-and-export --wait`): translation didn't settle within `--timeout`. Check manually with `localization key get-timeline-by-operation-id <operationId>` (the id is printed before the wait starts), then run `generate-uilm-file`/`uilm-export` yourself once ready rather than assuming translation failed.
538
- - `no_project_domain` (from `new web`): the project has no domains registered in Blocks. Add one from the portal, or pass `--app-domain` explicitly if the user already knows the intended value.
539
- - HTML returned from an API command means the command endpoint path is wrong and must be fixed in the CLI.
540
-
541
- ## Local Development Checks
542
-
543
- These are for contributors maintaining the package, not for normal AI package consumers:
544
-
545
- ```bash
546
- npm test
547
- npm pack --dry-run
548
- ```
549
-
550
- Live smoke checks after login:
551
-
552
- ```bash
553
- blocks projects list --json
554
- blocks iam me --json
555
- blocks data schema list --json
556
- ```
557
-
558
- ## Security Boundary
559
-
560
- The CLI may store secrets and tokens in the OS credential backend. Generated apps must not. The scaffolded app should receive only public runtime config such as API URL, project key, app domain, OIDC URL, and public OIDC client id.
1
+ # Blocks CLI Guide for AI Agents
2
+
3
+ This guide is for AI agents using the published `@seliseblocks/cli-os` npm package. The installed binary is `blocks`.
4
+
5
+ Use `blocks` as the control plane. If a capability exists in the CLI, call the CLI from the terminal instead of calling Blocks cloud APIs directly from ad hoc scripts or generated application code.
6
+
7
+ ## Install
8
+
9
+ Install the package in the environment where the agent will operate:
10
+
11
+ ```bash
12
+ npm install -g @seliseblocks/cli-os
13
+ ```
14
+
15
+ Verify the binary:
16
+
17
+ ```bash
18
+ blocks --version
19
+ blocks --help
20
+ ```
21
+
22
+ For local package development only, contributors may run `node bin/run.js ...` from the source repository. AI agents consuming the npm package should use `blocks ...`.
23
+
24
+ ## Global Options
25
+
26
+ Namespaced commands accept either spaces or colons, e.g. `blocks data schema list` and
27
+ `blocks data:schema:list` are equivalent. Global options available on every command:
28
+
29
+ - `--json` - print machine-readable JSON where supported.
30
+ - `--api-url <url>` - override the Blocks API URL for this command.
31
+ - `--account <name>` - use a named account profile; default is implicit.
32
+ - `--project <tenantId>` - use a project tenant for project-scoped commands.
33
+ - `--dry-run` / `--yes` - see Operating Rules below.
34
+
35
+ Use `blocks --help` (no subcommand) as command ground truth for what exists. Do not probe an individual subcommand with `<command> --help` to check its flags - most subcommands don't recognize `--help` as special and just run their real logic with it as a no-op argument (e.g. `login --help` performs an actual login attempt; `new web <name> --help` runs real arg validation). If you need a subcommand's full flag list, read this guide's section for it or infer from `--dry-run`/error output instead.
36
+
37
+ ## Operating Rules
38
+
39
+ - Use `blocks ...` for all supported Blocks OS, IAM, Data, Release, and scaffold operations.
40
+ - Prefer `--json` for automation and parsing.
41
+ - Use `--dry-run` before any mutating command.
42
+ - Do not run real mutating cloud commands unless the user explicitly approved the exact action.
43
+ - Never print, commit, scaffold, or document access tokens, refresh tokens, cookies, JWTs, or other secrets.
44
+ - Treat any secret pasted into chat or logs as exposed and rotate it before production use.
45
+ - Generated apps must not contain CLI tokens.
46
+ - If a CLI command returns an error, fix or report the CLI path. Do not bypass the CLI with a one-off API request when the command exists.
47
+
48
+ ## Login
49
+
50
+ Device-code login uses the packaged OS client id. It prints a verification URL and user code, opens the browser to the verification page when possible, then polls until approved:
51
+
52
+ ```bash
53
+ blocks login
54
+ ```
55
+
56
+ Check current auth state:
57
+
58
+ ```bash
59
+ blocks auth status --json
60
+ ```
61
+
62
+ If local auth state is stale or corrupted (Windows profile change, machine migration, Keychain reset), clear local auth state and log in again:
63
+
64
+ ```bash
65
+ blocks auth remove <account>
66
+ blocks login
67
+ ```
68
+
69
+ Use `blocks logout` to revoke the current refresh token when possible and remove local session data. Use `blocks auth refresh --json` to force account token refresh, and `blocks auth refresh --project --json` after a project session already exists.
70
+
71
+ Run health checks without mutation:
72
+
73
+ ```bash
74
+ blocks doctor --json
75
+ ```
76
+
77
+ ## Project Workflow
78
+
79
+ List projects:
80
+
81
+ ```bash
82
+ blocks projects list --json
83
+ ```
84
+
85
+ `projects create` is currently disabled in this build (commented out pending a product decision) - do not tell users it's available, and do not try to work around its absence with a raw API call. Projects must already exist (created from the Blocks portal) before selecting one below.
86
+
87
+ Select a project:
88
+
89
+ ```bash
90
+ blocks use <projectTenantId>
91
+ ```
92
+
93
+ Read the selected project:
94
+
95
+ ```bash
96
+ blocks projects get --json
97
+ ```
98
+
99
+ If an impersonated project token has expired or failed and re-running the command doesn't
100
+ recover, clear the selection and reselect to force re-impersonation:
101
+
102
+ ```bash
103
+ blocks deselect
104
+ blocks use <projectTenantId>
105
+ ```
106
+
107
+ ## Scaffold a Web App
108
+
109
+ Generate a React/Vite Blocks app. All of `--x-blocks-key`, `--app-domain`, and `--client-id` are optional now - they're resolved from the selected project when omitted:
110
+
111
+ ```bash
112
+ blocks use <projectTenantId> # if not already selected
113
+ blocks new web <appName>
114
+ ```
115
+
116
+ This is interactive when a value isn't already known: if the project has more than one registered domain you're prompted to choose; the OIDC client is offered as a pick-list of the project's existing clients, plus "create a new one now" (prompts only for display name + redirect URI, defaulting to `https://<appDomain>/login/callback`) or "skip, register later." Do not fabricate a client id or domain value yourself.
117
+
118
+ **An AI agent running this non-interactively will hang on these prompts** - there's no stdin to answer "Choose 1-3:" from an automated process. Before running `new web`, gather the values yourself and pass them explicitly:
119
+
120
+ ```bash
121
+ blocks projects get --json # see the project's domain(s) under project.applications
122
+ blocks auth oidc-clients list --json # see existing OIDC clients, if any
123
+ ```
124
+
125
+ Then run with explicit flags so no prompt is reached:
126
+
127
+ ```bash
128
+ blocks new web <appName> --x-blocks-key <projectTenantId> --app-domain <appDomainOrUrl> --client-id <publicOidcClientId>
129
+ ```
130
+
131
+ `new web` also accepts `--blocks-api-url <url>` and `--oidc-url <url>`, same as `sdk client`
132
+ below. When `--blocks-api-url` is omitted, the scaffold derives it from the app domain as
133
+ `https://blocksapi.<registrable-domain>`; for example `https://dqrsf.slsblx.com` becomes
134
+ `https://blocksapi.slsblx.com`. Pass `--blocks-api-url` only when targeting a non-default Blocks gateway. `--oidc-url` defaults to `https://iam.seliseblocks.com`.
135
+
136
+ Validate the scaffold:
137
+
138
+ ```bash
139
+ cd <appName>
140
+ npm install
141
+ npm run build
142
+ ```
143
+
144
+ Do not pass CLI auth state to the scaffolded app. Browser apps must use a public OIDC client and the SDK hosted IdP flow: `blocksClient.auth.idp.redirectToProvider()` on login click and `blocksClient.auth.idp.callback()` on `/login/callback`.
145
+
146
+ `--app-domain` is the app's real Blocks domain/origin, for example `https://dbpdba.seliseblocks.com`. The generated `.env` keeps that full value as `VITE_BLOCKS_APP_DOMAIN` and derives the local dev host without a scheme as `VITE_BLOCKS_DEV_HOST=dbpdba.seliseblocks.com`.
147
+
148
+ For local browser login on the real host domain:
149
+
150
+ 1. Add `127.0.0.1 <VITE_BLOCKS_DEV_HOST>` to the hosts file.
151
+ 2. Run `npm install`.
152
+ 3. Run `npm run cert`.
153
+ 4. Run `npm run dev`.
154
+ 5. Open `https://<VITE_BLOCKS_DEV_HOST>:5173`, not plain `http://`.
155
+
156
+ The generated cert script uses the `selfsigned` Node dependency, so it works from normal PowerShell after `npm install`; do not tell Windows users to switch to Git Bash just for OpenSSL. If hosted login or secure cookies fail locally, confirm the app is opened with the HTTPS dev URL from `VITE_BLOCKS_DEV_HOST`.
157
+
158
+ ## SDK Client (read-only)
159
+
160
+ `sdk client` answers "I want to use the Blocks SDK - show me the client." It resolves this project's `@seliseblocks/client` config (same values `new web` scaffolds an app with) and prints a ready-to-paste `createBlocksClient(...)` snippet - **it never writes a file or mutates anything**. To scaffold a full app instead, use `new web` above.
161
+
162
+ ```bash
163
+ blocks sdk client --x-blocks-key <projectTenantId> --app-domain <appDomainOrUrl> --client-id <publicOidcClientId> --blocks-api-url https://api.seliseblocks.com
164
+ ```
165
+
166
+ Unlike `new web`, `sdk client` keeps `--blocks-api-url` defaulted to `https://api.seliseblocks.com`; only pass it explicitly if your project uses a different gateway URL. Passing both `--app-domain` and `--client-id` skips the project lookup entirely, so it needs no CLI login at all - useful for a quick, non-interactive check. Omit either one and it resolves from the selected project instead (auto-picks when there's exactly one match, otherwise lists the options and asks you to pass the flag explicitly - it does not prompt or create anything, since this command is read-only). Use `--json` for the resolved values instead of the snippet.
167
+
168
+ ## Skills
169
+
170
+ `skill list [--json]` / `skill show <name> [--json]` / `skill add <name> [--dir <path>]` read this package's bundled copy of `blocks-skills/*/SKILL.md` - local-only, no cloud calls. `skill add` copies a skill's **entire directory** (`SKILL.md` plus any supporting files, e.g. `flows/*.md`) into `<dir>/<name>/` (default `./blocks-skills`) in the current directory, for pulling a single skill into a project outside this monorepo. `skill list`'s human-readable output (and the "unknown skill" error from `show`/`add`) both point at the full public skill catalog, in case the locally bundled set is out of date. As with any skill file, verify command names against this guide or `blocks --help` before running them - skills are conversational context, not command ground truth.
171
+
172
+ ## IAM, MFA, and Auth Admin
173
+
174
+ `iam me` reads the CLI operator's own account identity (bootstrapping, not a project resource):
175
+
176
+ ```bash
177
+ blocks iam me --json
178
+ ```
179
+
180
+ Every other command below is project-scoped: it requires a project already selected (`blocks use <tenantId>`) and always calls IAM through an impersonated project token - never the account token, and never something you construct yourself. If no project is selected, the command fails with `project_not_selected`; run `blocks use <tenantId>` first (see Agent Failure Handling).
181
+
182
+ Command families (run `blocks --help` for the full flag reference on each):
183
+
184
+ - `iam users *`, `iam email available` - list/get/create/update/activate/deactivate, access grant/revoke, existence and email-availability checks.
185
+ - `iam roles *` - list/get/create/update, assign-permissions, assignable. `assign-permissions` accepts permission resource strings and resolves them to itemIds before sending IAM's id-based mutation.
186
+ - `iam permissions *` - list/get/create/update, by-severity.
187
+ - `iam resources *` - resource groups and feature flags (read-only).
188
+ - `iam organizations *` - list/get/create/update, `my`, and organization config get/save.
189
+ - `iam signup-settings *` - get/save tenant signup policy.
190
+ - `mfa config *`, `mfa totp *`, `mfa generate`/`resend`/`verify`, `mfa method set`, `mfa disable`, `mfa backup-codes *` - tenant MFA policy plus enrollment/verification/backup-code flows.
191
+ - `mfa totp enable --mfa-type <n>` - composed TOTP enrollment: `totp setup` → prints the QR/secret → `totp verify-setup` → `method set` → `backup-codes generate`, one confirmation. Prefer this over running the individual steps. `--mfa-type` is required and not defaulted - the tenant-specific integer meaning "TOTP" isn't documented anywhere in this CLI; don't guess it, ask the user or check `mfa config get`. **Prompts interactively for the verification code unless `--code <c>` is given** - an agent running this non-interactively must supply `--code` (from wherever the user's authenticator app output is captured) or it will hang waiting on stdin. Deliberately excludes `mfa config save` (a separate tenant-wide admin policy, not part of one user's enrollment).
192
+ - `auth idp *` - identity provider (SSO/OIDC) configuration: list/get/create/update/delete/status.
193
+ - `auth config *` - AuthController tenant config (token lifetimes, lockout policy, etc.).
194
+ - `auth client-credentials *` - machine-to-machine client credentials: list/save/delete.
195
+ - `auth oidc-clients *` - OIDC client app registrations: list/get/save (upsert)/delete/rotate-secret.
196
+
197
+ Rules:
198
+
199
+ - Use `--dry-run` before any mutating command in these families, the same as Data/Localization/Release, then `--yes` only after explicit approval.
200
+ - Rich payloads (identity provider config, OIDC client config, user/role/permission create-update bodies, etc.) accept `--body '<json>'` or `--file <path.json>` on top of the documented convenience flags - use whichever is easier for the exact fields you need to set.
201
+ - `auth idp create`/`update`, `auth client-credentials save`, and `auth oidc-clients save`/`rotate-secret` can return a `client_secret` shown only once. Never print, log, commit, or otherwise persist it outside what the user explicitly asked to store; treat that response the same as any other CLI-managed secret.
202
+ - Do not add IAM/MFA/Auth admin behavior outside these supported CLI commands unless the CLI package is explicitly extended and tested.
203
+
204
+ ## Data
205
+
206
+ Check the data-source configuration first. Most projects run on Blocks-managed storage by default, so this is usually the only `data config *` command you need:
207
+
208
+ ```bash
209
+ blocks data config get --json
210
+ ```
211
+
212
+ Only create/update a data source configuration after explicit user approval - it points the project's Data Gateway at a different (external) database, which is a deliberate, rare action:
213
+
214
+ ```bash
215
+ blocks data config create --connection-string "<cs>" --database-name "<name>" --dry-run --json
216
+ blocks data config create --connection-string "<cs>" --database-name "<name>" --yes --json
217
+ blocks data config update --item-id <id> --connection-string "<cs>" --dry-run --json
218
+ blocks data config update --item-id <id> --connection-string "<cs>" --yes --json
219
+ ```
220
+
221
+ Validate local files:
222
+
223
+ ```bash
224
+ blocks data validate --json
225
+ ```
226
+
227
+ List schemas:
228
+
229
+ ```bash
230
+ blocks data schema list --json
231
+ ```
232
+
233
+ Pull schemas:
234
+
235
+ ```bash
236
+ blocks data schema pull --json
237
+ ```
238
+
239
+ Push schemas only after dry-run and approval:
240
+
241
+ ```bash
242
+ blocks data schema push --dry-run --json
243
+ blocks data schema push --yes --json
244
+ ```
245
+
246
+ Pull rules:
247
+
248
+ ```bash
249
+ blocks data rules pull --json
250
+ ```
251
+
252
+ Deploy rules only after dry-run and approval:
253
+
254
+ ```bash
255
+ blocks data rules deploy --dry-run --json
256
+ blocks data rules deploy --yes --json
257
+ ```
258
+
259
+ Reload Data schema configuration only after approval:
260
+
261
+ ```bash
262
+ blocks data reload --dry-run --json
263
+ blocks data reload --yes --json
264
+ ```
265
+
266
+ **Prefer `data sync` over running validate/push/deploy/reload separately.** It composes all four (validate → `schema push` → `rules deploy` → `data reload`) behind one confirmation, and it's the only way to guarantee the reload actually happens - nothing else calls it automatically, so schema/rule changes pushed without a following `data reload` can sit staged without going live:
267
+
268
+ ```bash
269
+ blocks data sync --dry-run --json
270
+ blocks data sync --yes --json
271
+ ```
272
+
273
+ It validates first and hard-fails with no API calls made if schemas or the rules file don't parse/validate. It prints 3 separate step outputs (one per underlying command), not one combined JSON document - parse each block in sequence if you need machine-readable results from all three.
274
+
275
+ ### Raw Data API
276
+
277
+ `validate`/`schema list`/`schema pull`/`schema push`/`rules pull`/`rules deploy`/`reload` above cover the common file-oriented workflow. The rest of `/data/v4/*` is exposed directly, project-scoped with an impersonated project token only. Run `blocks --help` for the full flag reference on each; command families:
278
+
279
+ - `data schema get`/`get-by-name`/`aggregation`/`change-logs`/`delete` - single-schema lookup by id or collection name, access-level aggregation summary, unadapted change logs (cleared by `data reload`), and irreversible delete. `schema get` also prints the schema's exact GraphQL operation names in non-`--json` output; do not guess pluralized names -- generated names are naive string concatenation (`Company` -> `getCompanys`, not `getCompanies`), read them from `querySchema`/`mutationSchemas` instead.
280
+ - `data schema info list`/`save`/`update` + `data schema fields` - a two-step alternative to `schema push` (create/update schema metadata, then add/update field definitions separately). Prefer the file-oriented `schema push` workflow for normal authoring; use these only for a targeted metadata or field-only change without touching the local schema JSON.
281
+ - `data rules policy get`/`delete` - read or delete one data-access policy without a full `rules pull`/edit/`rules deploy` round-trip.
282
+ - `data validation list`/`get`/`by-schema`/`by-schema-field`/`save`/`delete` - field-level validation rules. No file-oriented workflow exists for these (no local JSON file to pull/push). `save` is an upsert (omit `--item-id` to create, pass it to update) and requires a `validations` array passed via `--body`/`--file` - there's no scalar flag for it, e.g. `--body '{"validations":[{"type":1,"value":"^[0-9]+$","isActive":true}]}'`.
283
+ - `data files *` - permission-aware storage object tree: upload/download, directory CRUD/move, cursor list/search, versions, copy/move/rename, trash/restore/purge, shared objects, and access policies/inheritance.
284
+
285
+ Same rules as everywhere else: `--dry-run` before any mutating command, then `--yes` only after explicit approval.
286
+
287
+ **`--file` means two different things depending on the command.** Everywhere else in this CLI (`--body '<json>'`/`--file <path.json>`), `--file` is a JSON payload file read by `jsonBodyFlag`. On the `data files *` upload commands (`upload-to-url`, `upload-to-local-storage`), `--file` is instead the local binary file to read and upload - there is no JSON payload involved. Don't conflate the two: passing a JSON path to `data files upload-to-local-storage --file` uploads the JSON text as the file's bytes, it does not set a request body.
288
+
289
+ **Prefer the composed `data files upload` over the manual steps below.** For cloud storage it creates the file/version metadata and PUTs the bytes; for local storage it performs one multipart call. Either path creates the visible object directly—there is no DMS registration step:
290
+
291
+ ```bash
292
+ blocks data files upload --file ./invoice.pdf --access-modifier Public --dry-run --json
293
+ blocks data files upload --file ./invoice.pdf --access-modifier Public --yes --json
294
+ blocks data files upload --file ./invoice.pdf --local-storage --yes --json # local-storage-backed projects
295
+ ```
296
+
297
+ Manual cloud-storage upload, if you need the intermediate steps for some reason (two calls):
298
+
299
+ ```bash
300
+ blocks data files presigned-upload-url --name invoice.pdf --access-modifier Public --dry-run --json
301
+ blocks data files presigned-upload-url --name invoice.pdf --access-modifier Public --yes --json
302
+ # take the returned uploadUrl and fileId, then:
303
+ blocks data files upload-to-url --url "<uploadUrl>" --file ./invoice.pdf --content-type application/pdf --dry-run --json
304
+ blocks data files upload-to-url --url "<uploadUrl>" --file ./invoice.pdf --content-type application/pdf --yes --json
305
+ ```
306
+
307
+ Manual local-storage upload (one call):
308
+
309
+ ```bash
310
+ blocks data files upload-to-local-storage --file ./invoice.pdf --access-modifier Public --dry-run --json
311
+ blocks data files upload-to-local-storage --file ./invoice.pdf --access-modifier Public --yes --json
312
+ ```
313
+
314
+ Browse the resulting object tree with cursor pagination. Deletion defaults to trash:
315
+
316
+ ```bash
317
+ blocks data files list --parent-id <directoryId> --limit 50 --json
318
+ blocks data files search invoice --directory-id <directoryId> --json
319
+ blocks data files delete <fileId> --dry-run --json
320
+ blocks data files delete <fileId> --yes --json
321
+ blocks data files trash --json
322
+ blocks data files restore <fileId> --dry-run --json
323
+ ```
324
+
325
+ ## Localization
326
+
327
+ Generate or update local i18n dictionaries as JSON, then let the CLI sync them to Blocks Localization. Do not ask humans to manually copy keys into the portal.
328
+
329
+ Default file convention:
330
+
331
+ ```text
332
+ blocks/localization/<module>.<language>.json
333
+ ```
334
+
335
+ Example:
336
+
337
+ ```json
338
+ {
339
+ "dashboard.title": "Dashboard",
340
+ "products.empty": "No products found"
341
+ }
342
+ ```
343
+
344
+ Nested JSON is accepted on input and flattened before validation:
345
+
346
+ ```json
347
+ {
348
+ "dashboard": {
349
+ "title": "Dashboard"
350
+ }
351
+ }
352
+ ```
353
+
354
+ Validate first:
355
+
356
+ ```bash
357
+ blocks localization validate --module common --language en --json
358
+ ```
359
+
360
+ Push only after dry-run and approval:
361
+
362
+ ```bash
363
+ blocks localization push --module common --language en --dry-run --json
364
+ blocks localization push --module common --language en --yes --json
365
+ ```
366
+
367
+ Pull published cloud localization when local fallback files need to be refreshed:
368
+
369
+ ```bash
370
+ blocks localization pull --module common --language en --json
371
+ ```
372
+
373
+ Use Localization gateway v4 paths without `/api`: `/localization/v4/Module/Gets`, `/localization/v4/Module/Save`, `/localization/v4/Key/SaveKeys`, and `/localization/v4/Key/GetCloudUilmFile`.
374
+
375
+ ### Raw Localization API
376
+
377
+ `validate`/`push`/`pull` above cover the common i18n file workflow. Every other `/localization/v4/*` endpoint is also exposed directly, project-scoped with an impersonated project token only (never the account token). Run `blocks --help` for the full flag reference on each; command families:
378
+
379
+ - `localization assistant translation-suggestion` - AI translation suggestion for a single string (`--source-text`, `--destination-language`, optional glossary/context flags).
380
+ - `localization config get-webhook`/`save-webhook` - tenant webhook config for localization change notifications.
381
+ - `localization glossary save`/`list`/`get`/`suggested`/`delete` - glossary term CRUD and AI-suggested glossary lookup.
382
+ - `localization key save`/`list`/`get-by-names`/`get`/`delete`/`delete-keys` - key CRUD and search beyond the bulk `push`/`pull` flow.
383
+ - `localization key get-timeline`/`get-localization-timeline`/`get-timeline-by-operation-id`/`rollback` - key/tenant change history and rollback.
384
+ - `localization key get-uilm-file`/`generate-uilm-file`/`uilm-import`/`uilm-export`/`get-uilm-exported-files`/`get-language-file-generation-history` - UILM language-file generation and import/export jobs.
385
+ - `localization key translate-all`/`translate-key`/`translate-keys` - trigger AI machine translation for a module or specific keys.
386
+ - `localization key translate-and-export --module-id <id> [--wait]` - composed: `translate-all` → `generate-uilm-file` → `uilm-export`. Prefer this over running the three by hand. `--wait` polls translation progress first via a self-generated correlation id (translation is async and has no documented "done" field, so this is a best-effort heuristic - it prints the raw response every poll); without `--wait` it just fires all three back to back like running them manually in sequence.
387
+ - `localization language save`/`list`/`list-for-tenant`/`delete`/`set-default` - tenant language catalog management.
388
+ - `localization module save`/`list`/`list-for-tenant`/`tag-glossary` - module CRUD and glossary tagging.
389
+
390
+ Same rules as everywhere else: `--dry-run` before any mutating command, then `--yes` only after explicit approval; rich payloads accept `--body '<json>'`/`--file <path.json>` on top of the documented convenience flags. `localization config save-webhook`'s `--secret` is redacted in `--dry-run` output only - treat the live response as a secret.
391
+
392
+ ## Mail
393
+
394
+ Project-scoped SMTP/inbound mail configuration, templates, and mailbox reads via `/os/v4/Mail/*`:
395
+
396
+ ```bash
397
+ blocks mail config list --json
398
+ blocks mail config get <name> --json
399
+ blocks mail config save --name <n> --host <h> --port <p> --enable-ssl \
400
+ --sender-name <n> --sender-address <addr> --account-password <p> --dry-run --json
401
+ blocks mail config save --configuration-id <id> ... --yes --json # update
402
+ blocks mail config delete <configurationId> --dry-run --json
403
+ blocks mail config duplicate <configurationId> --dry-run --json
404
+
405
+ blocks mail template list --configuration-id <id> --json
406
+ blocks mail template get <itemId> --json
407
+ blocks mail template save --configuration-id <id> --name <n> --language <l> \
408
+ --subject <s> --template-body <html> --dry-run --json
409
+ blocks mail template delete <itemId> --dry-run --json
410
+ blocks mail template clone <itemId> --name <n> --dry-run --json
411
+
412
+ blocks mail mailbox list --configuration-id <id> --json
413
+ blocks mail mailbox get <messageId> --json
414
+ ```
415
+
416
+ Treat `--account-password` as a secret; the CLI redacts it in `--dry-run` output but the live response is still yours to protect.
417
+
418
+ Sending mail is a separate surface, `/logic/v4/Mail/Send` and `/logic/v4/Mail/SendToAny` (not `/os/v4`):
419
+
420
+ ```bash
421
+ blocks mail send --to a@example.com,b@example.com --purpose welcome --language en \
422
+ --subject-data-context '{"firstName":"Ada"}' --dry-run --json
423
+ blocks mail send --to a@example.com --purpose welcome --language en --yes --json
424
+
425
+ blocks mail sendtoany --to a@example.com --purpose welcome --language en \
426
+ --is-test-mail --dry-run --json
427
+ ```
428
+
429
+ `--project-key` defaults to the selected project's tenant id; pass it explicitly only to target a different one. `--attachments`/`--subject-data-context`/`--body-data-context` take raw JSON.
430
+
431
+ ## Notification
432
+
433
+ Project-scoped notification channel configuration via `/os/v4/Notification/*`:
434
+
435
+ ```bash
436
+ blocks notification list --json
437
+ blocks notification get <itemId> --json
438
+ blocks notification save --name <n> --channel <0|1> --type <0-3> --dry-run --json
439
+ blocks notification save --name <n> --channel <0|1> --type <0-3> --update --yes --json
440
+ blocks notification delete <itemId> --dry-run --json
441
+ ```
442
+
443
+ `--channel` and `--type` are raw numeric enum values from the Blocks OS API (`NotifierTypes`, `NotificationReceiverTypes`) — the API does not publish names for them.
444
+
445
+ ## Notifier
446
+
447
+ Real-time/offline notification sends and inbox reads via `/logic/v4/Notifier/*` — distinct from
448
+ `notification` above, which manages channel *configuration*, not sending:
449
+
450
+ ```bash
451
+ blocks notifier notify --user-ids u1,u2 --response-key status --response-value ok --dry-run --json
452
+ blocks notifier notify --roles admin --denormalized-payload '{"orderId":"123"}' \
453
+ --save-denormalized-payload-as-object --yes --json
454
+ blocks notifier notify --subscription-filters '[{"context":"orders","actionName":"created","value":"*"}]' --yes --json
455
+
456
+ blocks notifier list --unread-only --page 1 --page-size 20 --json
457
+ blocks notifier unread --user-id <id> --context orders --action-name created --json
458
+ blocks notifier mark-read <notificationId> --dry-run --json
459
+ blocks notifier mark-all-read --dry-run --json
460
+ ```
461
+
462
+ Target `notify` with at least one of `--user-ids`/`--roles`/`--subscription-filters`. `notifier unread`
463
+ sends its filter as query parameters even though swagger documents that endpoint as GET with a JSON
464
+ body, which the Fetch spec forbids — the CLI and SDK both flatten it into the query string instead.
465
+
466
+ ## Secrets
467
+
468
+ Generic tenant secret storage via `/os/v4/Secrets/*` (e.g. captcha provider config):
469
+
470
+ ```bash
471
+ blocks secrets get captcha --json
472
+ blocks secrets save --secret-key captcha \
473
+ --key-value-pairs '{"isEnable":"true","provider":"recaptcha","captchaKey":"...","captchaSecret":"..."}' \
474
+ --dry-run --json
475
+ blocks secrets save --secret-key captcha --item-id <itemId> --key-value-pairs '{...}' --yes --json
476
+ ```
477
+
478
+ `--key-value-pairs` is a flat JSON object of provider-specific fields — its shape depends entirely on
479
+ `--secret-key` (there's no fixed schema across secrets). `save` is an upsert: omit `--item-id` to
480
+ create, pass it to update. Fields that look like secrets/keys are redacted in `--dry-run` output only.
481
+
482
+ ## Storage
483
+
484
+ Project-scoped storage backend configuration via `/os/v4/Storage/*`:
485
+
486
+ ```bash
487
+ blocks storage config list --json
488
+ blocks storage config get <name> --json
489
+ blocks storage config save --name <n> --strategy <s> --secret-key <k> --access-key <k> --dry-run --json
490
+ blocks storage config save --item-id <id> --update ... --yes --json # update
491
+ blocks storage config delete <name> --dry-run --json
492
+ ```
493
+
494
+ `--secret-key`, `--access-key`, `--password`, and `--connection-string` are secrets; the CLI redacts them in `--dry-run` output only.
495
+
496
+ ## Release
497
+
498
+ `release deploy` needs no `--repo-id` - it resolves the repo linked to the selected project (`Project/GetAsset`) and that repo's connected branch (`Build/repo-details`) on its own, and refuses to deploy if the connected branch doesn't match the project's environment name. Trigger a deploy only after dry-run and approval:
499
+
500
+ ```bash
501
+ blocks release deploy --dry-run --json
502
+ blocks release deploy --yes --json
503
+ blocks release deploy --domain <customDomain> --yes --json # also sets the custom deployment domain first
504
+ blocks release deploy --yes --wait --json # poll until the build finishes instead of returning the build id
505
+ ```
506
+
507
+ If no repo is linked yet, the command fails with `repo_not_linked` - that requires GitHub OAuth, so it can only be done from the Blocks portal; do not attempt to link a repo from the CLI.
508
+
509
+ `--wait` polls `/release/v4/api/Build` (same data `release status` reads) every `--poll-interval` seconds (default 10) until a terminal-looking state is detected or `--timeout` elapses (default 900s). There's no documented status field/enum for this endpoint, so "terminal" is a best-effort text match (success/fail/complete/cancel/etc. anywhere in the response) - the raw JSON is printed every poll, so verify against that rather than trusting the heuristic blindly. Without `--wait`, `release deploy` returns immediately with just a build id, same as before.
510
+
511
+ Read build status:
512
+
513
+ ```bash
514
+ blocks release status <buildId> --json
515
+ blocks release builds get <buildId> --json
516
+ ```
517
+
518
+ List builds for a repository (repoId is optional now - omit it to resolve from the selected project's linked repo assets, auto-picked if there's exactly one, otherwise interactively prompted, which will hang a non-interactive agent - pass `--repo-id` explicitly if you don't already know there's exactly one):
519
+
520
+ ```bash
521
+ blocks release builds list --repo-id <repoId> --json
522
+ ```
523
+
524
+ ## Agent Failure Handling
525
+
526
+ - `not_logged_in`: run `blocks login`, then `blocks projects list`, then `blocks use <tenantId>`.
527
+ - `refresh_token_rejected`: run `blocks login`.
528
+ - `refresh_network_error`: check the network and configured OIDC URL, then retry.
529
+ - `auth_repair_required`: inspect `blocks auth status --json`; if local storage is unreadable or stale, run `blocks auth remove <account>`, then `blocks auth status --json` and `blocks login`.
530
+ - `project_not_selected`: run `blocks projects list`, then `blocks use <projectTenantId>`.
531
+ - `api_auth_failed`: run `blocks auth status --json`, then login again. If the failure is specifically a stale/expired impersonated project token rather than the account token, `blocks deselect` followed by `blocks use <tenantId>` re-impersonates without a full re-login.
532
+ - `repo_not_linked` (from `release deploy`): no repo is linked to this project. This needs GitHub OAuth - tell the user to link it from the Blocks portal, do not retry from the CLI.
533
+ - `repo_ambiguous` (from `release deploy`): multiple repos are linked and none is named for the current environment. Tell the user to check the project's repo links in the portal.
534
+ - `repo_not_found` (from `release deploy`): the linked asset's repo id doesn't exist in blocks-release. Tell the user to check the project's repo link in the portal.
535
+ - `branch_environment_mismatch` (from `release deploy`): the connected repo's branch doesn't match this environment's name. The message states the branch found and the environment required - do not retry; the repo's connected branch must be fixed first.
536
+ - `build_wait_timeout` (from `release deploy --wait`): the build didn't reach a detected terminal state within `--timeout`. The deploy itself already succeeded (this only affects the wait) - check manually with `release status <buildId>` rather than assuming failure.
537
+ - `translation_wait_timeout` (from `localization key translate-and-export --wait`): translation didn't settle within `--timeout`. Check manually with `localization key get-timeline-by-operation-id <operationId>` (the id is printed before the wait starts), then run `generate-uilm-file`/`uilm-export` yourself once ready rather than assuming translation failed.
538
+ - `no_project_domain` (from `new web`): the project has no domains registered in Blocks. Add one from the portal, or pass `--app-domain` explicitly if the user already knows the intended value.
539
+ - HTML returned from an API command means the command endpoint path is wrong and must be fixed in the CLI.
540
+
541
+ ## Local Development Checks
542
+
543
+ These are for contributors maintaining the package, not for normal AI package consumers:
544
+
545
+ ```bash
546
+ npm test
547
+ npm pack --dry-run
548
+ ```
549
+
550
+ Live smoke checks after login:
551
+
552
+ ```bash
553
+ blocks projects list --json
554
+ blocks iam me --json
555
+ blocks data schema list --json
556
+ ```
557
+
558
+ ## Security Boundary
559
+
560
+ The CLI may store secrets and tokens in the OS credential backend. Generated apps must not. The scaffolded app should receive only public runtime config such as API URL, project key, app domain, OIDC URL, and public OIDC client id.