@seliseblocks/cli-os 0.2.11 → 0.2.12

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.
Files changed (47) hide show
  1. package/AI_USAGE_GUIDE.md +551 -560
  2. package/README.md +171 -173
  3. package/dist/commands/auth/refresh.js +21 -2
  4. package/dist/commands/mfa/generate.js +7 -4
  5. package/dist/commands/mfa/method-set.js +13 -4
  6. package/dist/commands/mfa/totp-enable.d.ts +3 -3
  7. package/dist/commands/mfa/totp-enable.js +4 -4
  8. package/dist/commands/mfa/verify.js +4 -2
  9. package/dist/commands/projects/create.js +110 -16
  10. package/dist/index.js +696 -703
  11. package/package.json +2 -2
  12. package/dist/commands/sdk/client.d.ts +0 -1
  13. package/dist/commands/sdk/client.js +0 -99
  14. package/dist/commands/skill/add.d.ts +0 -1
  15. package/dist/commands/skill/add.js +0 -19
  16. package/dist/commands/skill/list.d.ts +0 -1
  17. package/dist/commands/skill/list.js +0 -15
  18. package/dist/commands/skill/show.d.ts +0 -1
  19. package/dist/commands/skill/show.js +0 -15
  20. package/dist/lib/skills.d.ts +0 -17
  21. package/dist/lib/skills.js +0 -69
  22. package/dist/skills/blocks-data-gateway-configuration/SKILL.md +0 -204
  23. package/dist/skills/blocks-data-gateway-crud/SKILL.md +0 -223
  24. package/dist/skills/blocks-data-storage/SKILL.md +0 -253
  25. package/dist/skills/blocks-data-storage/flows/object-management.md +0 -124
  26. package/dist/skills/blocks-frontend-local-https/SKILL.md +0 -100
  27. package/dist/skills/blocks-iam-access-control/SKILL.md +0 -49
  28. package/dist/skills/blocks-iam-access-control/flows/feature-gating.md +0 -38
  29. package/dist/skills/blocks-iam-access-control/flows/manage-roles-permissions.md +0 -110
  30. package/dist/skills/blocks-iam-account/SKILL.md +0 -169
  31. package/dist/skills/blocks-iam-mfa/SKILL.md +0 -124
  32. package/dist/skills/blocks-iam-organizations/SKILL.md +0 -43
  33. package/dist/skills/blocks-iam-organizations/flows/admin-mutations.md +0 -89
  34. package/dist/skills/blocks-iam-organizations/flows/read-and-switch.md +0 -57
  35. package/dist/skills/blocks-iam-sso-oidc-configuration/SKILL.md +0 -105
  36. package/dist/skills/blocks-iam-sso-oidc-implementation/SKILL.md +0 -80
  37. package/dist/skills/blocks-iam-users/SKILL.md +0 -131
  38. package/dist/skills/blocks-localization-configuration/SKILL.md +0 -149
  39. package/dist/skills/blocks-localization-implementation/SKILL.md +0 -63
  40. package/dist/skills/blocks-mail/SKILL.md +0 -95
  41. package/dist/skills/blocks-notification/SKILL.md +0 -69
  42. package/dist/skills/blocks-notifier/SKILL.md +0 -107
  43. package/dist/skills/blocks-onboarding/SKILL.md +0 -77
  44. package/dist/skills/blocks-release-deployment/SKILL.md +0 -81
  45. package/dist/skills/blocks-secrets/SKILL.md +0 -81
  46. package/dist/skills/blocks-storage-configuration/SKILL.md +0 -93
  47. package/dist/skills/lint.mjs +0 -168
package/README.md CHANGED
@@ -1,173 +1,171 @@
1
- # Blocks CLI
2
-
3
- CLI for SELISE Blocks Cloud.
4
-
5
- - Package: [`@seliseblocks/cli-os`](https://www.npmjs.com/package/@seliseblocks/cli-os)
6
- - Binary: `blocks`
7
-
8
- ## Setup
9
-
10
- Install the npm package where you want to operate the CLI:
11
-
12
- ```bash
13
- npm install -g @seliseblocks/cli-os@latest
14
- blocks --version
15
- ```
16
-
17
- Then log in (device-code flow - prints a verification URL and code, opens
18
- your browser to the verification page when possible so you only need to
19
- click approve, then polls until approved):
20
-
21
- ```bash
22
- blocks login
23
- ```
24
-
25
- For source development in this repository:
26
-
27
- ```bash
28
- npm install
29
- npm run build
30
- node bin/run.js --version
31
- ```
32
-
33
- ## Commands
34
-
35
- Namespaced commands accept either spaces or colons, e.g. `blocks data schema list` and
36
- `blocks data:schema:list` are equivalent.
37
-
38
- Global options available on every command:
39
-
40
- | Option | Description |
41
- |---|---|
42
- | `--version` | Print CLI version. |
43
- | `--json` | Print machine-readable JSON where supported. |
44
- | `--api-url <url>` | Override the Blocks API URL for this command. |
45
- | `--account <name>` | Use a named account profile; default is implicit. |
46
- | `--project <tenantId>` | Use a project tenant for project-scoped commands. |
47
- | `--dry-run` | Show planned mutation without calling the API. |
48
- | `--yes` | Skip mutation confirmation after explicit approval. |
49
-
50
- | Command | Description |
51
- |---|---|
52
- | `blocks init` | Create local Blocks workspace files: `blocks.json`, data schema/rules folders, and `.env.example`. |
53
- | `blocks doctor [--json]` | Check Node.js, OIDC config, token cache, selected project, and config file locations. Does not mutate cloud resources. |
54
- | `blocks login` | Device-code login. Prints a verification URL and user code, opens the browser to the verification page when possible so you only need to click approve, then polls until the device is authorized; stores account tokens and auto-refreshes later. |
55
- | `blocks auth status [--json]` | Show only whether account/project access and refresh tokens are missing, valid, expired, or available. Does not print account config values. |
56
- | `blocks auth refresh [--project] [--json]` | Force account token refresh, or project token refresh with `--project`. |
57
- | `blocks auth remove <account>` | Clear cached tokens and stored local credentials for that account. The packaged default OS account is restored from package defaults. |
58
- | `blocks logout` | Revoke the current refresh token when possible and remove local session data. |
59
- | `blocks projects list [--json]` | List accessible Blocks projects via `/os/v4/Project/Gets` using the account token. Read-only. |
60
- | `blocks projects get [tenantId] [--deployment] [--json]` | Read one project from `Project/Gets`. Uses selected project when `tenantId` is omitted. Pass `--deployment` to also include the environment, tenantGroupId, and linked repo assets that `release deploy` resolves internally. Read-only. |
61
- | `blocks projects create` | **Currently disabled** (commented out, not deleted) - the dispatch entry, import, and help text were removed pending a product decision. Do not tell users this command is available. |
62
- | `blocks use <tenantId>` | Save the selected project tenant globally and in `blocks.json` when present. Does not call cloud APIs. |
63
- | `blocks deselect` | Clear the selected project tenant (globally and in `blocks.json`) and drop its cached impersonation token. Use this to recover when an impersonated project token has expired or failed, then run `blocks use <tenantId>` again to reselect and re-impersonate. |
64
- | `blocks iam me [--json]` | Read the current user from IAM using the account token (CLI operator identity, not a project resource). |
65
- | `blocks iam users *`, `iam email available`, `iam roles *`, `iam permissions *`, `iam resources *`, `iam organizations *`, `iam signup-settings *` | Full IAM admin surface for the selected project (users, roles, permissions, resource metadata, organizations and their config, signup settings). Project-scoped: requires a selected project and always uses an impersonated project token, never the account token. Mutating commands support `--dry-run`/`--yes`; rich payloads accept `--body '<json>'`/`--file <path>` on top of common convenience flags. Run `blocks --help` for the full command/flag list. |
66
- | `blocks mfa config *`, `mfa totp *`, `mfa generate`, `mfa resend`, `mfa verify`, `mfa method set`, `mfa disable`, `mfa backup-codes *` | Project-scoped MFA admin and self-service surface (tenant MFA policy, TOTP enrollment, OTP challenge/verify, method switch, backup codes). Same project-selection and impersonation-only rules as IAM above. |
67
- | `blocks mfa totp enable --mfa-type <n> [--code <c>]` | Composed TOTP enrollment: `totp setup` → prints the QR/secret → prompts for the code (or `--code` to skip the prompt) → `totp verify-setup` → `method set` → `backup-codes generate`, one confirmation. `--mfa-type` is required and not defaulted - the tenant-specific integer meaning "TOTP" isn't documented anywhere in this CLI. Deliberately excludes `mfa config save` (a separate, tenant-wide admin policy, not part of one user's enrollment). |
68
- | `blocks auth idp *`, `auth config *`, `auth client-credentials *`, `auth oidc-clients *` | Auth admin surface: identity providers, AuthController tenant config, machine-to-machine client credentials, and OIDC client app registrations. Project-scoped, impersonated project token only. `idp delete`, `client-credentials save`/`delete`, and `oidc-clients save`/`delete`/`rotate-secret` return or handle secrets shown only once - treat CLI output as sensitive and never log it. |
69
- | `blocks mail config *`, `mail template *`, `mail mailbox *` | Project-scoped mail admin surface via `/os/v4/Mail/*`: SMTP/inbound configuration upsert/delete/duplicate, mail template CRUD/clone, and mailbox message reads. Impersonated project token only. `mail config save`'s `--account-password` is redacted in `--dry-run` output only. |
70
- | `blocks mail send [--to a,b] [--purpose <p>] [--language <c>] ...` / `mail sendtoany [same, plus --is-test-mail]` | Send an email via `/logic/v4/Mail/Send` / `/logic/v4/Mail/SendToAny`. Impersonated project token only; `--project-key` defaults to the selected project. |
71
- | `blocks notification *` | Project-scoped notification channel configuration via `/os/v4/Notification/*` (list/get/save/delete). Impersonated project token only. `--channel`/`--type` are raw numeric enum values from the API. |
72
- | `blocks notifier notify`, `notifier list`, `notifier unread`, `notifier mark-read`, `notifier mark-all-read` | Real-time/offline notification sends and inbox reads via `/logic/v4/Notifier/*` - distinct from `notification` above, which manages channel configuration, not sending. Impersonated project token only. `notify` targets `--user-ids`/`--roles`/`--subscription-filters`; `unread` sends its filter as query parameters even though swagger documents that endpoint as GET with a JSON body, which the Fetch spec forbids. |
73
- | `blocks secrets get <secretKey>` / `secrets save --secret-key <key> --key-value-pairs '<json>'` | Generic tenant secret storage via `/os/v4/Secrets/*` (e.g. captcha provider config). Impersonated project token only. `save` is an upsert - omit `--item-id` to create, pass it to update. |
74
- | `blocks storage config *` | Project-scoped storage backend configuration via `/os/v4/Storage/*` (list/get/save/delete). Impersonated project token only. `--secret-key`/`--access-key`/`--password`/`--connection-string` are redacted in `--dry-run` output only. |
75
- | `blocks data validate [--json]` | Validate local `blocks/data/schemas/*.json` and `blocks/data/rules.json` before pushing. Local-only. |
76
- | `blocks data schema list [--page <n>] [--page-size <n>] [--json]` | List project schemas via `/data/v4/schemas` using an impersonated project token. Read-only. `--page`/`--page-size` default to 1/100. Validates the response envelope and fails clearly on an unexpected shape instead of treating it as an empty list. |
77
- | `blocks data schema pull [--json]` | Download every project schema (paging through all of them) into `blocks/data/schemas/*.json`. Strips the API `id`, project identifiers, access-policy metadata, and platform-managed fields (`ItemId`, `CreatedDate`, etc.) so the file is portable and re-pushable as-is. Writes local files only. |
78
- | `blocks data schema push [--dry-run] [--yes] [--json]` | Create or update project schemas via `/data/v4/schemas/define`. Mutating. Looks up the destination project's schema by name (never trusts a local `id`/`itemId`, which may belong to another project) and uses PUT with the destination id when found, POST otherwise; warns when a local id is ignored, and fails clearly (nonzero exit) instead of treating an empty/`204` response as success. |
79
- | `blocks data rules pull [--json]` | Download data-access policies into `blocks/data/rules.json` in the CLI's portable format (`schemaName`, not the API's `entityName`; no `itemId`/`schemaId`). Writes local files only. |
80
- | `blocks data rules deploy [--dry-run] [--yes] [--json]` | Apply schema security and data-access policies. Mutating; supports dry-run and confirmation. Resolves each policy's destination schema id and any existing policy id by name -- never reuses a source-project id -- and fails if a policy targets a schema that doesn't exist yet in the destination project. |
81
- | `blocks data reload [--dry-run] [--yes] [--json]` | Reload Data schema configuration so staged schema/rule changes become live. Mutating; calls POST `/data/v4/schema-configurations/reload`. |
82
- | `blocks data sync [--dry-run] [--yes] [--json]` | Composed flow: validate local schemas/rules → `data schema push` → `data rules deploy` → `data reload`, one confirmation. Use this instead of running the three separately - nothing else calls `data reload` automatically, so pushed changes can otherwise sit staged without going live. Prints 3 separate step outputs, not one combined document. |
83
- | `blocks data config get/create/update` | Tenant data-source configuration via `/data/v4/configurations` - check `get` first, it defaults to Blocks-managed storage; `create`/`update` point the gateway at an external database. Impersonated project token only. |
84
- | `blocks data schema get/get-by-name/aggregation/change-logs/delete/fields/info *` | Raw `/data/v4/schemas*` API beyond the file-oriented `list/pull/push` above: single-schema/by-name lookups, access-level aggregation, unadapted change logs, delete, and a metadata-first (`info *`) plus field-only (`fields`) alternative to `push`. Run `blocks --help` for the full flag reference. `schema get` additionally prints the schema's exact GraphQL operation names in non-`--json` output -- generated names are naive string concatenation (`get{QuerySchema}`, `insert{SchemaName}`, `insertMany{SchemaName}`, etc.), not English pluralization, so e.g. `Company` becomes `getCompanys`/`Companys`, not `Companies`. |
85
- | `blocks data rules policy get/delete` | Single data-access policy read/delete via `/data/v4/data-access/policy/*`, alongside the bulk file-oriented `rules pull/deploy` above. |
86
- | `blocks data validation list/get/by-schema/by-schema-field/save/delete` | Field-level validation rules via `/data/v4/data-validations*`. No file-oriented workflow exists for this yet; `save` is an upsert (omit `--item-id` to create) and requires a `validations` array via `--body`/`--file`. |
87
- | `blocks data files upload --file <path> [--local-storage]` | Composed upload: cloud storage creates the file/version record and PUTs the bytes; local storage uses one multipart request. The file is immediately part of the object tree—there is no DMS registration step. |
88
- | `blocks data files *` | Full storage object-tree surface: file get/upload/metadata/delete/version/copy/move/rename, directory CRUD/move, cursor-paginated list/search/shared/trash, restore/purge, and access policy/share/inheritance commands. Delete defaults to trash; pass `--permanent` explicitly to remove data. Run `blocks --help` for flags. |
89
- | `blocks localization validate --module <name> --language <culture> [--file <path>] [--json]` | Validate a local i18n JSON dictionary. Supports nested JSON input and checks the flattened key/value set locally. |
90
- | `blocks localization push --module <name> --language <culture> [--file <path>] [--route <route>] [--context <text>] [--dry-run] [--yes] [--json]` | Create or update Localization keys from local i18n JSON via `/localization/v4/Key/SaveKeys`. Creates the module first through `/localization/v4/Module/Save` when missing. |
91
- | `blocks localization pull --module <name> --language <culture> [--out <path>] [--json]` | Download published cloud localization via `/localization/v4/Key/GetCloudUilmFile` and write a local JSON dictionary. |
92
- | `blocks localization key translate-and-export --module-id <id> [--wait]` | Composed flow: `translate-all` → `generate-uilm-file` → `uilm-export`. With `--wait`, polls translation progress (via a self-generated correlation id) before generating/exporting, since translation runs asynchronously; without it, fires all 3 back to back. Prints one output block per step. |
93
- | `blocks localization assistant translation-suggestion`, `localization config *`, `localization glossary *`, `localization key *`, `localization language *`, `localization module *` | Full raw `/localization/v4/*` API surface (AI translation suggestions, tenant webhook config, glossary CRUD, key CRUD/search/timeline/translate/UILM import-export/rollback, language CRUD, module CRUD/glossary tagging) alongside the file-oriented validate/push/pull commands above. Project-scoped, impersonated project token only. Run `blocks --help` for the full command/flag list. |
94
- | `blocks release deploy [--domain <customDomain>] [--wait] [--poll-interval <s>] [--timeout <s>] [--dry-run] [--yes] [--json]` | Deploy the selected project's environment. No `--repo-id` needed - resolves the linked repo via `Project/GetAsset` and its connected branch via `Build/repo-details`, and aborts if that branch doesn't match the environment name. Pass `--domain` to also set the custom deployment domain first. Pass `--wait` to poll `release status` on the resulting build until it reaches a terminal state instead of returning immediately with just a build id. Mutating; no artifact upload is performed by this CLI. |
95
- | `blocks release status <buildId> [--json]` | Read Release build status by build id. Read-only. |
96
- | `blocks release builds list [repoId] [--repo-id <repoId>] [--json]` | List Release build details for a repository. When `repoId` is omitted, resolves it from the selected project's linked repo assets - auto-picked if there's exactly one, otherwise you're prompted to choose. Read-only. |
97
- | `blocks release builds get <buildId> [--json]` | Alias for `release status`. Read-only. |
98
- | `blocks new web <name> [--app-domain <domain>] [--client-id <oidcClientId>] [--x-blocks-key <tenantId>] [--blocks-api-url <url>] [--oidc-url <url>]` | Create a Vite React starter app that talks to Blocks exclusively through `@seliseblocks/client` (a single `createBlocksClient()` instance) using the SDK hosted IdP flow: `blocksClient.auth.idp.redirectToProvider()` on login click and `blocksClient.auth.idp.callback()` on `/login/callback`. Includes route guards, auto-refresh through `auth.oidc.refreshToken()`, live `auth`/`iam`/`data`/`localization` SDK examples, environment config, and safe `.gitignore` defaults. Uses the selected project (see `use`) unless `--x-blocks-key` overrides it. `--app-domain` and `--client-id` are resolved from the project record when omitted: the domain auto-picks if the project has exactly one, otherwise you're prompted to choose; the OIDC client is picked from the project's existing clients, or you can create a minimal one (display name + redirect URI) on the spot, or skip and register one later from the portal or `auth oidc-clients save`. When `--blocks-api-url` is omitted, `new web` derives it from the app domain as `https://blocksapi.<registrable-domain>`; for example `https://dqrsf.slsblx.com` becomes `https://blocksapi.slsblx.com`. Pass a different Data/IAM/Localization/OS gateway URL explicitly only if your project uses a non-default one. `--oidc-url` defaults to `https://iam.seliseblocks.com`. |
99
- | `blocks skill list [--json]` / `skill show <name> [--json]` / `skill add <name> [--dir <path>]` | Local-only, no cloud calls: list/print the bundled `blocks-skills/*/SKILL.md` agent context docs, or copy a skill's entire directory (`SKILL.md` plus any supporting files) into `<dir>/<name>/` (default `./blocks-skills`) for use in a project outside this monorepo. `list`'s output and `show`/`add`'s "unknown skill" error both point at the full public skill catalog in case the bundled set is out of date. |
100
- | `blocks sdk client [--app-domain <domain>] [--client-id <oidcClientId>] [--x-blocks-key <tenantId>] [--blocks-api-url <url>] [--oidc-url <url>] [--json]` | Read-only: resolves this project's `@seliseblocks/client` config and prints a ready-to-paste `createBlocksClient(...)` snippet. Unlike `new web`, the API URL defaults to `https://api.seliseblocks.com` unless `--blocks-api-url` is passed. Passing both `--app-domain` and `--client-id` skips the project lookup entirely (no login required). Writes nothing - use `new web` to scaffold a full app. |
101
-
102
- Use `--json` on commands when AI or automation needs machine-readable output. Use `--dry-run` before mutations and `--yes` only after approval.
103
-
104
- For agent-specific operating rules and command sequences, see [AI_USAGE_GUIDE.md](AI_USAGE_GUIDE.md).
105
-
106
- ### Scaffolded Web App Local HTTPS
107
-
108
- For `blocks new web`, `--app-domain` should be the app's real Blocks domain/origin, for example `https://dbpdba.seliseblocks.com`. The generated app 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`.
109
-
110
- Browser login uses the hosted Blocks IAM IdP flow and secure cookies, so local testing on the project domain must run over HTTPS:
111
-
112
- ```bash
113
- cd <appName>
114
- npm install
115
- npm run cert
116
- npm run dev
117
- ```
118
-
119
- Add the generated `VITE_BLOCKS_DEV_HOST` to your hosts file, for example:
120
-
121
- ```text
122
- 127.0.0.1 dbpdba.seliseblocks.com
123
- ```
124
-
125
- Then open `https://<VITE_BLOCKS_DEV_HOST>:5173`, not plain `http://`. The generated cert script uses a Node dependency, so it works from normal PowerShell after `npm install`; OpenSSL/Git Bash is not required.
126
-
127
- OIDC account settings are saved in the OS-specific config directory. Access and refresh tokens are stored in an OS-aware secure backend when available, and are refreshed automatically before cloud commands when a refresh token is available.
128
-
129
- OAuth tokens use an OS-aware credential backend:
130
-
131
- | OS | Credential backend |
132
- |---|---|
133
- | Windows | DPAPI-encrypted value in the CLI secret metadata file, scoped to the current Windows user. |
134
- | macOS | Keychain generic password for the `seliseblocks-cli` service. |
135
- | Linux | Secret Service through `secret-tool` when available. |
136
- | Fallback | `0600` file storage in the CLI config directory. Set `BLOCKS_SECRET_STORE=file` to force this mode for CI or minimal containers. |
137
-
138
- Use `blocks doctor` or `blocks auth status` to see which backend is active. Tokens are never printed by CLI status commands.
139
-
140
- If the active OS credential backend cannot decrypt old local auth state after a Windows profile change, machine migration, Keychain reset, or corrupted token cache, clear local auth state and log in again:
141
-
142
- ```bash
143
- blocks auth remove <account>
144
- blocks login
145
- ```
146
-
147
- ## Workspace
148
-
149
- `blocks init` creates:
150
-
151
- ```text
152
- blocks.json
153
- blocks/
154
- data/
155
- schemas/
156
- rules.json
157
- .env.example
158
- ```
159
-
160
- Localization dictionaries are not created by `init` - the default path is `blocks/localization/<module>.<language>.json`, for example `blocks/localization/common.en.json`, and the `blocks/localization/` folder is created lazily the first time `blocks localization pull` writes to it. AI agents can create or update that file directly (before `push`, which only reads it), run `blocks localization validate`, then push it to the Localization service with `blocks localization push --dry-run` followed by `--yes` after approval. Gateway v4 routes do not include an `/api` segment.
161
-
162
- `blocks use <tenantId>` updates the selected project in global CLI state and `blocks.json` when present.
163
-
164
- `blocks release deploy` has no local config file - it needs a repo already linked to the project. Linking a repo requires GitHub OAuth, which only the Blocks portal can do; if none is linked, the command tells you so and stops.
165
-
166
- ## Boundaries
167
-
168
- - `iam me` reads the CLI operator's own account identity; every other `iam *`, `mfa *`, `auth idp *`/`auth config *`/`auth client-credentials *`/`auth oidc-clients *`, `mail *`, `notification *`, `notifier *`, `secrets *`, and `storage config *` command is project-scoped and requires a selected project (`blocks use <tenantId>`) plus an impersonated project token - none of them ever run against the account token.
169
- - Data covers tenant data-source configuration, schema/rules/reload/validate, field-level validation rules, and the storage object tree (`data files *`). Prefer the composed `data sync` and `data files upload` workflows.
170
- - Localization covers dictionary validate/pull/push plus the full raw `/localization/v4/*` API surface (assistant, config, glossary, key, language, module). Prefer `localization key translate-and-export` over running translate/generate/export by hand.
171
- - Release covers deploy trigger and build status/read commands only.
172
- - No direct artifact upload unless Blocks Release adds a confirmed artifact upload API.
173
- - `projects create` is currently disabled in this build (commented out pending a product decision) - don't reference it as available.
1
+ # Blocks CLI
2
+
3
+ CLI for SELISE Blocks Cloud.
4
+
5
+ - Package: [`@seliseblocks/cli-os`](https://www.npmjs.com/package/@seliseblocks/cli-os)
6
+ - Binary: `blocks`
7
+
8
+ ## Setup
9
+
10
+ Install the npm package where you want to operate the CLI:
11
+
12
+ ```bash
13
+ npm install -g @seliseblocks/cli-os@latest
14
+ blocks --version
15
+ ```
16
+
17
+ Then log in (device-code flow - prints a verification URL and code, opens
18
+ your browser to the verification page when possible so you only need to
19
+ click approve, then polls until approved):
20
+
21
+ ```bash
22
+ blocks login
23
+ ```
24
+
25
+ For source development in this repository:
26
+
27
+ ```bash
28
+ npm install
29
+ npm run build
30
+ node bin/run.js --version
31
+ ```
32
+
33
+ ## Commands
34
+
35
+ Namespaced commands accept either spaces or colons, e.g. `blocks data schema list` and
36
+ `blocks data:schema:list` are equivalent.
37
+
38
+ Global options available on every command:
39
+
40
+ | Option | Description |
41
+ |---|---|
42
+ | `--version` | Print CLI version. |
43
+ | `--json` | Print machine-readable JSON where supported. |
44
+ | `--api-url <url>` | Override the Blocks API URL for this command. |
45
+ | `--account <name>` | Use a named account profile; default is implicit. |
46
+ | `--project <tenantId>` | Use a project tenant for project-scoped commands. |
47
+ | `--dry-run` | Show planned mutation without calling the API. |
48
+ | `--yes` | Skip mutation confirmation after explicit approval. |
49
+
50
+ | Command | Description |
51
+ |---|---|
52
+ | `blocks init` | Create local Blocks workspace files: `blocks.json`, data schema/rules folders, and `.env.example`. |
53
+ | `blocks doctor [--json]` | Check Node.js, OIDC config, token cache, selected project, and config file locations. Does not mutate cloud resources. |
54
+ | `blocks login` | Device-code login. Prints a verification URL and user code, opens the browser to the verification page when possible so you only need to click approve, then polls until the device is authorized; stores account tokens and auto-refreshes later. |
55
+ | `blocks auth status [--json]` | Show only whether account/project access and refresh tokens are missing, valid, expired, or available. Does not print account config values. |
56
+ | `blocks auth refresh [--project] [--json]` | Force account token refresh, or project token refresh with `--project`. |
57
+ | `blocks auth remove <account>` | Clear cached tokens and stored local credentials for that account. The packaged default OS account is restored from package defaults. |
58
+ | `blocks logout` | Revoke the current refresh token when possible and remove local session data. |
59
+ | `blocks projects list [--json]` | List accessible Blocks projects via `/os/v4/Project/Gets` using the account token. Read-only. |
60
+ | `blocks projects get [tenantId] [--deployment] [--json]` | Read one project from `Project/Gets`. Uses selected project when `tenantId` is omitted. Pass `--deployment` to also include the environment, tenantGroupId, and linked repo assets that `release deploy` resolves internally. Read-only. |
61
+ | `blocks projects create <name> [--allow-duplicate-name] [--yes] [--dry-run] [--json]` | Create a new project via `/os/v4/Project/Create` with the account token (no project needs to be selected). Always creates **exactly one application, in the `dev` environment** - environment, domain, cookie domain, and production flag are not configurable, and the domain sent is a placeholder the platform replaces with the one it assigns. Confirms first because it accepts the Blocks terms on your behalf. Refuses a name already used by another project unless `--allow-duplicate-name` is passed, then verifies the result against `Project/Gets` and prints the new `tenantId`, `tenantGroupId`, and assigned domain. Does not select the project - run `blocks use <tenantId>` next. |
62
+ | `blocks use <tenantId>` | Save the selected project tenant globally and in `blocks.json` when present. Does not call cloud APIs. |
63
+ | `blocks deselect` | Clear the selected project tenant (globally and in `blocks.json`) and drop its cached impersonation token. Use this to recover when an impersonated project token has expired or failed, then run `blocks use <tenantId>` again to reselect and re-impersonate. |
64
+ | `blocks iam me [--json]` | Read the current user from IAM using the account token (CLI operator identity, not a project resource). |
65
+ | `blocks iam users *`, `iam email available`, `iam roles *`, `iam permissions *`, `iam resources *`, `iam organizations *`, `iam signup-settings *` | Full IAM admin surface for the selected project (users, roles, permissions, resource metadata, organizations and their config, signup settings). Project-scoped: requires a selected project and always uses an impersonated project token, never the account token. Mutating commands support `--dry-run`/`--yes`; rich payloads accept `--body '<json>'`/`--file <path>` on top of common convenience flags. Run `blocks --help` for the full command/flag list. |
66
+ | `blocks mfa config *`, `mfa totp *`, `mfa generate`, `mfa resend`, `mfa verify`, `mfa method set`, `mfa disable`, `mfa backup-codes *` | Project-scoped MFA admin and self-service surface (tenant MFA policy, TOTP enrollment, OTP challenge/verify, method switch, backup codes). Same project-selection and impersonation-only rules as IAM above. |
67
+ | `blocks mfa totp enable --mfa-type <n> [--code <c>]` | Composed TOTP enrollment: `totp setup` → prints the QR/secret → prompts for the code (or `--code` to skip the prompt) → `totp verify-setup` → `method set` → `backup-codes generate`, one confirmation. `--mfa-type` is required and not defaulted - `1` is TOTP in IAM's `UserMfaType` enum (`0` None, `1` TOTP, `2` Email, `3` Sms, `4` WhatsApp; only 1 and 2 have a provider). Deliberately excludes `mfa config save` (a separate, tenant-wide admin policy, not part of one user's enrollment). |
68
+ | `blocks auth idp *`, `auth config *`, `auth client-credentials *`, `auth oidc-clients *` | Auth admin surface: identity providers, AuthController tenant config, machine-to-machine client credentials, and OIDC client app registrations. Project-scoped, impersonated project token only. `idp delete`, `client-credentials save`/`delete`, and `oidc-clients save`/`delete`/`rotate-secret` return or handle secrets shown only once - treat CLI output as sensitive and never log it. |
69
+ | `blocks mail config *`, `mail template *`, `mail mailbox *` | Project-scoped mail admin surface via `/os/v4/Mail/*`: SMTP/inbound configuration upsert/delete/duplicate, mail template CRUD/clone, and mailbox message reads. Impersonated project token only. `mail config save`'s `--account-password` is redacted in `--dry-run` output only. |
70
+ | `blocks mail send [--to a,b] [--purpose <p>] [--language <c>] ...` / `mail sendtoany [same, plus --is-test-mail]` | Send an email via `/logic/v4/Mail/Send` / `/logic/v4/Mail/SendToAny`. Impersonated project token only; `--project-key` defaults to the selected project. |
71
+ | `blocks notification *` | Project-scoped notification channel configuration via `/os/v4/Notification/*` (list/get/save/delete). Impersonated project token only. `--channel`/`--type` are raw numeric enum values from the API. |
72
+ | `blocks notifier notify`, `notifier list`, `notifier unread`, `notifier mark-read`, `notifier mark-all-read` | Real-time/offline notification sends and inbox reads via `/logic/v4/Notifier/*` - distinct from `notification` above, which manages channel configuration, not sending. Impersonated project token only. `notify` targets `--user-ids`/`--roles`/`--subscription-filters`; `unread` sends its filter as query parameters even though swagger documents that endpoint as GET with a JSON body, which the Fetch spec forbids. |
73
+ | `blocks secrets get <secretKey>` / `secrets save --secret-key <key> --key-value-pairs '<json>'` | Generic tenant secret storage via `/os/v4/Secrets/*` (e.g. captcha provider config). Impersonated project token only. `save` is an upsert - omit `--item-id` to create, pass it to update. |
74
+ | `blocks storage config *` | Project-scoped storage backend configuration via `/os/v4/Storage/*` (list/get/save/delete). Impersonated project token only. `--secret-key`/`--access-key`/`--password`/`--connection-string` are redacted in `--dry-run` output only. |
75
+ | `blocks data validate [--json]` | Validate local `blocks/data/schemas/*.json` and `blocks/data/rules.json` before pushing. Local-only. |
76
+ | `blocks data schema list [--page <n>] [--page-size <n>] [--json]` | List project schemas via `/data/v4/schemas` using an impersonated project token. Read-only. `--page`/`--page-size` default to 1/100. Validates the response envelope and fails clearly on an unexpected shape instead of treating it as an empty list. |
77
+ | `blocks data schema pull [--json]` | Download every project schema (paging through all of them) into `blocks/data/schemas/*.json`. Strips the API `id`, project identifiers, access-policy metadata, and platform-managed fields (`ItemId`, `CreatedDate`, etc.) so the file is portable and re-pushable as-is. Writes local files only. |
78
+ | `blocks data schema push [--dry-run] [--yes] [--json]` | Create or update project schemas via `/data/v4/schemas/define`. Mutating. Looks up the destination project's schema by name (never trusts a local `id`/`itemId`, which may belong to another project) and uses PUT with the destination id when found, POST otherwise; warns when a local id is ignored, and fails clearly (nonzero exit) instead of treating an empty/`204` response as success. |
79
+ | `blocks data rules pull [--json]` | Download data-access policies into `blocks/data/rules.json` in the CLI's portable format (`schemaName`, not the API's `entityName`; no `itemId`/`schemaId`). Writes local files only. |
80
+ | `blocks data rules deploy [--dry-run] [--yes] [--json]` | Apply schema security and data-access policies. Mutating; supports dry-run and confirmation. Resolves each policy's destination schema id and any existing policy id by name -- never reuses a source-project id -- and fails if a policy targets a schema that doesn't exist yet in the destination project. |
81
+ | `blocks data reload [--dry-run] [--yes] [--json]` | Reload Data schema configuration so staged schema/rule changes become live. Mutating; calls POST `/data/v4/schema-configurations/reload`. |
82
+ | `blocks data sync [--dry-run] [--yes] [--json]` | Composed flow: validate local schemas/rules → `data schema push` → `data rules deploy` → `data reload`, one confirmation. Use this instead of running the three separately - nothing else calls `data reload` automatically, so pushed changes can otherwise sit staged without going live. Prints 3 separate step outputs, not one combined document. |
83
+ | `blocks data config get/create/update` | Tenant data-source configuration via `/data/v4/configurations` - check `get` first, it defaults to Blocks-managed storage; `create`/`update` point the gateway at an external database. Impersonated project token only. |
84
+ | `blocks data schema get/get-by-name/aggregation/change-logs/delete/fields/info *` | Raw `/data/v4/schemas*` API beyond the file-oriented `list/pull/push` above: single-schema/by-name lookups, access-level aggregation, unadapted change logs, delete, and a metadata-first (`info *`) plus field-only (`fields`) alternative to `push`. Run `blocks --help` for the full flag reference. `schema get` additionally prints the schema's exact GraphQL operation names in non-`--json` output -- generated names are naive string concatenation (`get{QuerySchema}`, `insert{SchemaName}`, `insertMany{SchemaName}`, etc.), not English pluralization, so e.g. `Company` becomes `getCompanys`/`Companys`, not `Companies`. |
85
+ | `blocks data rules policy get/delete` | Single data-access policy read/delete via `/data/v4/data-access/policy/*`, alongside the bulk file-oriented `rules pull/deploy` above. |
86
+ | `blocks data validation list/get/by-schema/by-schema-field/save/delete` | Field-level validation rules via `/data/v4/data-validations*`. No file-oriented workflow exists for this yet; `save` is an upsert (omit `--item-id` to create) and requires a `validations` array via `--body`/`--file`. |
87
+ | `blocks data files upload --file <path> [--local-storage]` | Composed upload: cloud storage creates the file/version record and PUTs the bytes; local storage uses one multipart request. The file is immediately part of the object tree—there is no DMS registration step. |
88
+ | `blocks data files *` | Full storage object-tree surface: file get/upload/metadata/delete/version/copy/move/rename, directory CRUD/move, cursor-paginated list/search/shared/trash, restore/purge, and access policy/share/inheritance commands. Delete defaults to trash; pass `--permanent` explicitly to remove data. Run `blocks --help` for flags. |
89
+ | `blocks localization validate --module <name> --language <culture> [--file <path>] [--json]` | Validate a local i18n JSON dictionary. Supports nested JSON input and checks the flattened key/value set locally. |
90
+ | `blocks localization push --module <name> --language <culture> [--file <path>] [--route <route>] [--context <text>] [--dry-run] [--yes] [--json]` | Create or update Localization keys from local i18n JSON via `/localization/v4/Key/SaveKeys`. Creates the module first through `/localization/v4/Module/Save` when missing. |
91
+ | `blocks localization pull --module <name> --language <culture> [--out <path>] [--json]` | Download published cloud localization via `/localization/v4/Key/GetCloudUilmFile` and write a local JSON dictionary. |
92
+ | `blocks localization key translate-and-export --module-id <id> [--wait]` | Composed flow: `translate-all` → `generate-uilm-file` → `uilm-export`. With `--wait`, polls translation progress (via a self-generated correlation id) before generating/exporting, since translation runs asynchronously; without it, fires all 3 back to back. Prints one output block per step. |
93
+ | `blocks localization assistant translation-suggestion`, `localization config *`, `localization glossary *`, `localization key *`, `localization language *`, `localization module *` | Full raw `/localization/v4/*` API surface (AI translation suggestions, tenant webhook config, glossary CRUD, key CRUD/search/timeline/translate/UILM import-export/rollback, language CRUD, module CRUD/glossary tagging) alongside the file-oriented validate/push/pull commands above. Project-scoped, impersonated project token only. Run `blocks --help` for the full command/flag list. |
94
+ | `blocks release deploy [--domain <customDomain>] [--wait] [--poll-interval <s>] [--timeout <s>] [--dry-run] [--yes] [--json]` | Deploy the selected project's environment. No `--repo-id` needed - resolves the linked repo via `Project/GetAsset` and its connected branch via `Build/repo-details`, and aborts if that branch doesn't match the environment name. Pass `--domain` to also set the custom deployment domain first. Pass `--wait` to poll `release status` on the resulting build until it reaches a terminal state instead of returning immediately with just a build id. Mutating; no artifact upload is performed by this CLI. |
95
+ | `blocks release status <buildId> [--json]` | Read Release build status by build id. Read-only. |
96
+ | `blocks release builds list [repoId] [--repo-id <repoId>] [--json]` | List Release build details for a repository. When `repoId` is omitted, resolves it from the selected project's linked repo assets - auto-picked if there's exactly one, otherwise you're prompted to choose. Read-only. |
97
+ | `blocks release builds get <buildId> [--json]` | Alias for `release status`. Read-only. |
98
+ | `blocks new web <name> [--app-domain <domain>] [--client-id <oidcClientId>] [--x-blocks-key <tenantId>] [--blocks-api-url <url>] [--oidc-url <url>]` | Create a Vite React starter app that talks to Blocks exclusively through `@seliseblocks/client` (a single `createBlocksClient()` instance) using the SDK hosted IdP flow: `blocksClient.auth.idp.redirectToProvider()` on login click and `blocksClient.auth.idp.callback()` on `/login/callback`. Includes route guards, auto-refresh through `auth.oidc.refreshToken()`, live `auth`/`iam`/`data`/`localization` SDK examples, environment config, and safe `.gitignore` defaults. Uses the selected project (see `use`) unless `--x-blocks-key` overrides it. `--app-domain` and `--client-id` are resolved from the project record when omitted: the domain auto-picks if the project has exactly one, otherwise you're prompted to choose; the OIDC client is picked from the project's existing clients, or you can create a minimal one (display name + redirect URI) on the spot, or skip and register one later from the portal or `auth oidc-clients save`. When `--blocks-api-url` is omitted, `new web` derives it from the app domain as `https://blocksapi.<registrable-domain>`; for example `https://dqrsf.slsblx.com` becomes `https://blocksapi.slsblx.com`. Pass a different Data/IAM/Localization/OS gateway URL explicitly only if your project uses a non-default one. `--oidc-url` defaults to `https://iam.seliseblocks.com`. |
99
+
100
+ Use `--json` on commands when AI or automation needs machine-readable output. Use `--dry-run` before mutations and `--yes` only after approval.
101
+
102
+ For agent-specific operating rules and command sequences, see [AI_USAGE_GUIDE.md](AI_USAGE_GUIDE.md).
103
+
104
+ ### Scaffolded Web App Local HTTPS
105
+
106
+ For `blocks new web`, `--app-domain` should be the app's real Blocks domain/origin, for example `https://dbpdba.seliseblocks.com`. The generated app 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`.
107
+
108
+ Browser login uses the hosted Blocks IAM IdP flow and secure cookies, so local testing on the project domain must run over HTTPS:
109
+
110
+ ```bash
111
+ cd <appName>
112
+ npm install
113
+ npm run cert
114
+ npm run dev
115
+ ```
116
+
117
+ Add the generated `VITE_BLOCKS_DEV_HOST` to your hosts file, for example:
118
+
119
+ ```text
120
+ 127.0.0.1 dbpdba.seliseblocks.com
121
+ ```
122
+
123
+ Then open `https://<VITE_BLOCKS_DEV_HOST>:5173`, not plain `http://`. The generated cert script uses a Node dependency, so it works from normal PowerShell after `npm install`; OpenSSL/Git Bash is not required.
124
+
125
+ OIDC account settings are saved in the OS-specific config directory. Access and refresh tokens are stored in an OS-aware secure backend when available, and are refreshed automatically before cloud commands when a refresh token is available.
126
+
127
+ OAuth tokens use an OS-aware credential backend:
128
+
129
+ | OS | Credential backend |
130
+ |---|---|
131
+ | Windows | DPAPI-encrypted value in the CLI secret metadata file, scoped to the current Windows user. |
132
+ | macOS | Keychain generic password for the `seliseblocks-cli` service. |
133
+ | Linux | Secret Service through `secret-tool` when available. |
134
+ | Fallback | `0600` file storage in the CLI config directory. Set `BLOCKS_SECRET_STORE=file` to force this mode for CI or minimal containers. |
135
+
136
+ Use `blocks doctor` or `blocks auth status` to see which backend is active. Tokens are never printed by CLI status commands.
137
+
138
+ If the active OS credential backend cannot decrypt old local auth state after a Windows profile change, machine migration, Keychain reset, or corrupted token cache, clear local auth state and log in again:
139
+
140
+ ```bash
141
+ blocks auth remove <account>
142
+ blocks login
143
+ ```
144
+
145
+ ## Workspace
146
+
147
+ `blocks init` creates:
148
+
149
+ ```text
150
+ blocks.json
151
+ blocks/
152
+ data/
153
+ schemas/
154
+ rules.json
155
+ .env.example
156
+ ```
157
+
158
+ Localization dictionaries are not created by `init` - the default path is `blocks/localization/<module>.<language>.json`, for example `blocks/localization/common.en.json`, and the `blocks/localization/` folder is created lazily the first time `blocks localization pull` writes to it. AI agents can create or update that file directly (before `push`, which only reads it), run `blocks localization validate`, then push it to the Localization service with `blocks localization push --dry-run` followed by `--yes` after approval. Gateway v4 routes do not include an `/api` segment.
159
+
160
+ `blocks use <tenantId>` updates the selected project in global CLI state and `blocks.json` when present.
161
+
162
+ `blocks release deploy` has no local config file - it needs a repo already linked to the project. Linking a repo requires GitHub OAuth, which only the Blocks portal can do; if none is linked, the command tells you so and stops.
163
+
164
+ ## Boundaries
165
+
166
+ - `iam me` reads the CLI operator's own account identity; every other `iam *`, `mfa *`, `auth idp *`/`auth config *`/`auth client-credentials *`/`auth oidc-clients *`, `mail *`, `notification *`, `notifier *`, `secrets *`, and `storage config *` command is project-scoped and requires a selected project (`blocks use <tenantId>`) plus an impersonated project token - none of them ever run against the account token.
167
+ - Data covers tenant data-source configuration, schema/rules/reload/validate, field-level validation rules, and the storage object tree (`data files *`). Prefer the composed `data sync` and `data files upload` workflows.
168
+ - Localization covers dictionary validate/pull/push plus the full raw `/localization/v4/*` API surface (assistant, config, glossary, key, language, module). Prefer `localization key translate-and-export` over running translate/generate/export by hand.
169
+ - Release covers deploy trigger and build status/read commands only.
170
+ - No direct artifact upload unless Blocks Release adds a confirmed artifact upload API.
171
+ - `projects create` creates a `dev`-only, single-application project. It cannot add environments to an existing project or create a non-`dev` one - those still go through the Blocks portal.
@@ -2,6 +2,7 @@ import { parseFlags, stringFlag } from "../../lib/args.js";
2
2
  import { getAccountSession, getImpersonatedProjectSession } from "../../lib/auth.js";
3
3
  import { readConfig } from "../../lib/config.js";
4
4
  import { readTokenStore } from "../../lib/token-store.js";
5
+ import { writeOutput } from "../../lib/output.js";
5
6
  export async function authRefresh(argv) {
6
7
  const { flags } = parseFlags(argv);
7
8
  const accountName = stringFlag(flags, "account");
@@ -9,9 +10,17 @@ export async function authRefresh(argv) {
9
10
  const config = await readConfig();
10
11
  const store = await readTokenStore();
11
12
  const account = await getAccountSession(accountName);
12
- console.log(`Account '${account.account}' session ready for tenant ${account.accountTenant}`);
13
- if (!project)
13
+ if (!project) {
14
+ // --json has to produce JSON on success too, not just on the error path, or an
15
+ // agent that asked for machine-readable output gets a prose line to parse.
16
+ if (flags.json) {
17
+ writeOutput({ account: account.account, accountTenant: account.accountTenant, refreshed: "account" }, flags);
18
+ }
19
+ else {
20
+ console.log(`Account '${account.account}' session ready for tenant ${account.accountTenant}`);
21
+ }
14
22
  return;
23
+ }
15
24
  if (!config.selectedProject?.tenantId) {
16
25
  throw new Error("No project selected. Run `blocks use <tenantId>` first.");
17
26
  }
@@ -20,5 +29,15 @@ export async function authRefresh(argv) {
20
29
  throw new Error("No project session exists yet. A service command must create impersonation first.");
21
30
  }
22
31
  const projectSession = await getImpersonatedProjectSession(account.account);
32
+ if (flags.json) {
33
+ writeOutput({
34
+ account: account.account,
35
+ accountTenant: account.accountTenant,
36
+ projectTenantId: projectSession.tenantId,
37
+ refreshed: "account+project"
38
+ }, flags);
39
+ return;
40
+ }
41
+ console.log(`Account '${account.account}' session ready for tenant ${account.accountTenant}`);
23
42
  console.log(`Project session ready for tenant ${projectSession.tenantId}`);
24
43
  }
@@ -1,13 +1,16 @@
1
- import { integerFlag, stringFlag } from "../../lib/args.js";
1
+ import { optionalIntegerFlag, stringFlag } from "../../lib/args.js";
2
2
  import { blocksRequest } from "../../lib/api.js";
3
3
  import { writeOutput } from "../../lib/output.js";
4
4
  import { requestContext } from "../../lib/request-context.js";
5
5
  import { parseCommand, selectedProject } from "../../lib/workspace.js";
6
6
  export async function mfaGenerate(argv) {
7
7
  const { args, flags } = parseCommand(argv);
8
- const mfaType = integerFlag(flags, "mfa-type", NaN) || Number(args[0]);
9
- if (!Number.isInteger(mfaType))
10
- throw new Error("Provide --mfa-type <n> (or a positional integer).");
8
+ // optionalIntegerFlag, not `integerFlag(...) || Number(args[0])`: mfaType 0 (None) is a
9
+ // valid enum value, and `0 || ...` silently fell through to the missing positional.
10
+ const mfaType = optionalIntegerFlag(flags, "mfa-type") ?? Number(args[0]);
11
+ if (!Number.isInteger(mfaType)) {
12
+ throw new Error("Provide --mfa-type <n> (or a positional integer). Only 1 (TOTP) and 2 (Email) have an OTP provider; IAM rejects the rest.");
13
+ }
11
14
  const projectKey = await selectedProject(flags);
12
15
  const result = await blocksRequest("/iam/v4/mfa/generate", {
13
16
  body: {
@@ -1,13 +1,22 @@
1
- import { integerFlag } from "../../lib/args.js";
1
+ import { optionalIntegerFlag } from "../../lib/args.js";
2
2
  import { blocksRequest } from "../../lib/api.js";
3
3
  import { writeOutput } from "../../lib/output.js";
4
4
  import { requestContext } from "../../lib/request-context.js";
5
5
  import { parseCommand, selectedProject } from "../../lib/workspace.js";
6
6
  export async function mfaMethodSet(argv) {
7
7
  const { args, flags } = parseCommand(argv);
8
- const mfaType = integerFlag(flags, "mfa-type", NaN) || Number(args[0]);
9
- if (!Number.isInteger(mfaType))
10
- throw new Error("Provide --mfa-type <n> (or a positional integer).");
8
+ // optionalIntegerFlag, not `integerFlag(...) || Number(args[0])`: mfaType 0 (None) is a
9
+ // valid enum value, and `0 || ...` silently fell through to the missing positional.
10
+ const mfaType = optionalIntegerFlag(flags, "mfa-type") ?? Number(args[0]);
11
+ if (!Number.isInteger(mfaType)) {
12
+ throw new Error("Provide --mfa-type <n> (or a positional integer). IAM's UserMfaType: 0 None, 1 TOTP, 2 Email, 3 Sms, 4 WhatsApp.");
13
+ }
14
+ // IAM's PUT /mfa/method only branches on TOTP and Email; every other value falls
15
+ // through to its disable path and turns the user's MFA off. Say so rather than
16
+ // letting "switch to SMS" silently unenroll them.
17
+ if (mfaType !== 1 && mfaType !== 2) {
18
+ console.warn(`Warning: IAM only switches to 1 (TOTP) or 2 (Email). --mfa-type ${mfaType} disables MFA for this user instead -- use 'blocks mfa disable' if that is what you meant.`);
19
+ }
11
20
  const projectKey = await selectedProject(flags);
12
21
  const result = await blocksRequest("/iam/v4/mfa/method", {
13
22
  body: { mfaType },
@@ -3,8 +3,8 @@
3
3
  * method to TOTP -> generate backup codes. This is one real enrollment sitting, not four
4
4
  * independent tasks -- nobody enables TOTP setup and comes back next week to verify it.
5
5
  *
6
- * --mfa-type is still required, not defaulted: the numeric MFA-method value that means
7
- * "TOTP" for a given tenant isn't documented anywhere in this CLI (mfa:method:set/mfa:generate
8
- * take the same raw, tenant-defined integer), so it's not safe to guess one here.
6
+ * --mfa-type is still required, not defaulted: it is IAM's UserMfaType value to make active
7
+ * after enrollment (1 = TOTP), the same integer mfa:method:set and mfa:generate take. Keeping
8
+ * it explicit means the caller states which method they are enrolling, not this command.
9
9
  */
10
10
  export declare function mfaTotpEnable(argv: string[]): Promise<void>;
@@ -12,15 +12,15 @@ import { mfaTotpVerifySetup } from "./totp-verify-setup.js";
12
12
  * method to TOTP -> generate backup codes. This is one real enrollment sitting, not four
13
13
  * independent tasks -- nobody enables TOTP setup and comes back next week to verify it.
14
14
  *
15
- * --mfa-type is still required, not defaulted: the numeric MFA-method value that means
16
- * "TOTP" for a given tenant isn't documented anywhere in this CLI (mfa:method:set/mfa:generate
17
- * take the same raw, tenant-defined integer), so it's not safe to guess one here.
15
+ * --mfa-type is still required, not defaulted: it is IAM's UserMfaType value to make active
16
+ * after enrollment (1 = TOTP), the same integer mfa:method:set and mfa:generate take. Keeping
17
+ * it explicit means the caller states which method they are enrolling, not this command.
18
18
  */
19
19
  export async function mfaTotpEnable(argv) {
20
20
  const { flags } = parseCommand(argv);
21
21
  const mfaType = integerFlag(flags, "mfa-type", NaN);
22
22
  if (!Number.isInteger(mfaType)) {
23
- throw new Error("Provide --mfa-type <n> -- the numeric MFA method value your tenant uses for TOTP (the same value 'mfa:method:set' expects).");
23
+ throw new Error("Provide --mfa-type <n> -- the UserMfaType value to make active after enrollment (1 is TOTP, the same value 'mfa:method:set' expects).");
24
24
  }
25
25
  if (booleanFlag(flags, "dry-run")) {
26
26
  writeOutput({
@@ -8,8 +8,10 @@ export async function mfaVerify(argv) {
8
8
  const mfaId = args[0] || stringFlag(flags, "mfa-id", { required: true });
9
9
  const verificationCode = args[1] || stringFlag(flags, "code", { required: true });
10
10
  const authType = integerFlag(flags, "auth-type", NaN);
11
- if (!Number.isInteger(authType))
12
- throw new Error("Provide --auth-type <n>.");
11
+ if (!Number.isInteger(authType)) {
12
+ // Same UserMfaType enum as --mfa-type -- IAM types this field as UserMfaType too.
13
+ throw new Error("Provide --auth-type <n> -- the method that issued the challenge: 1 TOTP, 2 Email.");
14
+ }
13
15
  const projectKey = await selectedProject(flags);
14
16
  const result = await blocksRequest("/iam/v4/mfa/verify", {
15
17
  body: {