@seliseblocks/cli-os 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commands/auth/config/save.js +16 -0
- package/dist/commands/new/web.js +16 -3
- package/dist/skills/blocks-iam-access-control/SKILL.md +49 -49
- package/dist/skills/blocks-iam-access-control/flows/feature-gating.md +38 -38
- package/dist/skills/blocks-iam-access-control/flows/manage-roles-permissions.md +110 -110
- package/dist/skills/blocks-iam-mfa/SKILL.md +124 -124
- package/dist/skills/blocks-iam-organizations/SKILL.md +43 -43
- package/dist/skills/blocks-iam-organizations/flows/admin-mutations.md +89 -89
- package/dist/skills/blocks-iam-organizations/flows/read-and-switch.md +57 -57
- package/dist/skills/blocks-iam-sso-oidc-configuration/SKILL.md +105 -105
- package/dist/skills/blocks-mail/SKILL.md +95 -95
- package/dist/skills/blocks-notification/SKILL.md +69 -69
- package/dist/skills/blocks-notifier/SKILL.md +107 -107
- package/dist/skills/blocks-onboarding/SKILL.md +6 -7
- package/dist/skills/blocks-release-deployment/SKILL.md +81 -81
- package/dist/skills/blocks-secrets/SKILL.md +81 -81
- package/dist/skills/lint.mjs +168 -168
- package/package.json +1 -1
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: blocks-notification
|
|
3
|
-
description: "Manage SELISE Blocks notification-channel configuration via `blocks notification *` — no SDK path exists (`@seliseblocks/client` has no `notification` namespace, only the unrelated real-time `notifier` surface). Covers `notification list`/`get` (read configs) and `notification save`/`delete` (project-scoped mutations, impersonated-project-token only). Use for 'list/get notification configs', 'save/update a channel', 'delete a config'. Always `--dry-run` before `--yes` on save/delete."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Blocks Notification — Channel Configuration
|
|
7
|
-
|
|
8
|
-
Manage notification-channel configuration through `blocks notification *`. This is **100% CLI, no SDK equivalent** — `@seliseblocks/client` has no `notification` namespace at all. It does have a `notifier` namespace (backing `blocks notifier *`), but that's a **different, unrelated surface**: `notifier` pushes real-time/offline notifications and reads a user's own inbox; `notification` (this skill) manages the tenant's notification *channel configuration* — which channel/method a notification type uses, not sending one. Never write a frontend/app-code path for channel configuration — it's always this CLI.
|
|
9
|
-
|
|
10
|
-
**Prerequisite:** a project is selected (`blocks use <tenantId>`, or pass `--project <tenantId>`) — see the blocks-onboarding skill. Every one of the four commands requires an impersonated project session; there is no account-level mode for any of them.
|
|
11
|
-
|
|
12
|
-
## Safe read commands
|
|
13
|
-
|
|
14
|
-
- **`blocks notification list [--page <n>] [--page-size <n>] [--sort-by <property>] [--sort-desc] [--filter <text>] [--json]`** — read-only.
|
|
15
|
-
- **`blocks notification get <itemId> [--json]`** — `itemId` may be given positionally or as `--id <itemId>`; one of the two is required (the command throws `Missing --id` if neither is given). Read-only.
|
|
16
|
-
|
|
17
|
-
Neither read command mutates anything and neither accepts `--dry-run` (there's nothing to preview — no request body is built, no confirmation gate exists for either).
|
|
18
|
-
|
|
19
|
-
## Mutating: save (create or update a channel config)
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
blocks notification save --name <n> --channel <0|1> --type <0-3> --dry-run --json # preview first
|
|
23
|
-
blocks notification save --name <n> --channel <0|1> --type <0-3> --yes --json # only after approval
|
|
24
|
-
blocks notification save --update --body '<json>' --yes --json # full custom payload
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
The request body is built by merging (in this order, later keys win) whatever `--body '<json>'` or `--file <path.json>` supplies, then these convenience flags layered on top (so an unset convenience flag never overwrites a value from `--body`/`--file`):
|
|
28
|
-
|
|
29
|
-
| Flag | Body field | Notes |
|
|
30
|
-
|---|---|---|
|
|
31
|
-
| `--channel <int>` | `channelToNotify` | Raw integer — the CLI does **not** validate or enum-check the value itself. |
|
|
32
|
-
| `--type <int>` | `notificationType` | Same — raw integer, no validation in the command. |
|
|
33
|
-
| `--enable-persistence` | `enablePersistence` | Boolean flag; only ever sent as `true` when passed — passing it absent never sends an explicit `false`. |
|
|
34
|
-
| `--update` | `isUpdateRequest` | Same true-only pattern as `--enable-persistence`. Set this when saving over an existing config rather than creating a new one. |
|
|
35
|
-
| `--name <text>` | `name` | |
|
|
36
|
-
| `--notify-method <text>` | `notifyMethod` | |
|
|
37
|
-
|
|
38
|
-
`--dry-run` prints the resolved request and returns — no network call, no confirmation prompt. Without `--dry-run`, it prompts interactively for a typed `yes` before sending (unless `--yes` is also passed); only then does it resolve the selected project and issue the request. Always show `--dry-run` output and get explicit approval before re-running with `--yes`.
|
|
39
|
-
|
|
40
|
-
`--channel`/`--type` don't have machine-readable names published by the API; the CLI's own usage docs note they correspond to the OS API's channel/receiver-type enums (`--channel` roughly `0|1`, `--type` roughly `0-3`) — treat those ranges as documentation convention, not something this command enforces, and ask the user for the exact intended value rather than guessing one.
|
|
41
|
-
|
|
42
|
-
## Mutating: delete
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
blocks notification delete <itemId> --dry-run --json
|
|
46
|
-
blocks notification delete <itemId> --yes --json
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Same `itemId` resolution as `get` — positional arg or `--id`, one required. `--dry-run` prints the resolved query and returns; otherwise it goes through the same confirmation gate as `save` (interactive `yes` prompt unless `--yes` is passed) before resolving the project and sending the request.
|
|
50
|
-
|
|
51
|
-
## Gotchas
|
|
52
|
-
|
|
53
|
-
- **No SDK path, ever.** If asked "how do I manage notification channels from my app," the answer is: you don't — this is CLI-only, human/CI-operated configuration, not something to wire into frontend or backend app code.
|
|
54
|
-
- **`notification` and `notifier` are not the same thing.** `notification save/list/get/delete` (this skill) configures *which channel/method* a notification type uses. `notifier notify/list/unread/mark-read/mark-all-read` *sends* notifications and reads a user's inbox — a separate command family with its own commands, not covered here. Don't answer a "send a notification" request with `notification save`, and don't answer a "configure the notification channel" request with `notifier`.
|
|
55
|
-
- **`list` and `get` have no `--dry-run`.** Only `save` and `delete` build a request that's worth previewing; the two read commands hit the API directly. Don't tell a user to `--dry-run` a `list` or `get` call.
|
|
56
|
-
- **`--enable-persistence` and `--update` are true-only flags.** Passing them sets the field to `true`; not passing them omits the field entirely (never sends an explicit `false`). If a user wants to explicitly *unset* persistence or force a plain create, that has to go through `--body`/`--file` directly, not the convenience flag.
|
|
57
|
-
- **`--channel`/`--type` are unvalidated raw integers.** The command will happily send any integer you give it — there's no local check against the underlying enums. Confirm the intended value with the user (or check the Blocks portal/API docs) rather than inventing one.
|
|
58
|
-
- **`itemId` for `get`/`delete` is always required**, positional or `--id` — never guessed. Ask the user rather than assuming a value.
|
|
59
|
-
- **Every command is project-scoped.** All four require a resolved project and an impersonated project token; behavior follows whichever project is currently selected via `blocks use` (or an explicit `--project` override).
|
|
60
|
-
- **`--dry-run` before `--yes`, always**, on `save` and `delete` — same discipline as every other mutating `blocks` command in this pack.
|
|
61
|
-
|
|
62
|
-
## Example trigger prompts
|
|
63
|
-
|
|
64
|
-
- "List the notification channel configs for this project."
|
|
65
|
-
- "Get notification config `<itemId>`."
|
|
66
|
-
- "Save a new notification config named `<name>` on channel 0, type 1." → preview with `--dry-run` first.
|
|
67
|
-
- "Update the existing `<name>` notification config." → add `--update`, still `--dry-run` before `--yes`.
|
|
68
|
-
- "Delete notification config `<itemId>`."
|
|
69
|
-
- "Send a notification to these users" / "show me a user's unread notifications" → not this skill; that's `blocks notifier *`, a different command family for sending/reading, not channel configuration.
|
|
1
|
+
---
|
|
2
|
+
name: blocks-notification
|
|
3
|
+
description: "Manage SELISE Blocks notification-channel configuration via `blocks notification *` — no SDK path exists (`@seliseblocks/client` has no `notification` namespace, only the unrelated real-time `notifier` surface). Covers `notification list`/`get` (read configs) and `notification save`/`delete` (project-scoped mutations, impersonated-project-token only). Use for 'list/get notification configs', 'save/update a channel', 'delete a config'. Always `--dry-run` before `--yes` on save/delete."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Blocks Notification — Channel Configuration
|
|
7
|
+
|
|
8
|
+
Manage notification-channel configuration through `blocks notification *`. This is **100% CLI, no SDK equivalent** — `@seliseblocks/client` has no `notification` namespace at all. It does have a `notifier` namespace (backing `blocks notifier *`), but that's a **different, unrelated surface**: `notifier` pushes real-time/offline notifications and reads a user's own inbox; `notification` (this skill) manages the tenant's notification *channel configuration* — which channel/method a notification type uses, not sending one. Never write a frontend/app-code path for channel configuration — it's always this CLI.
|
|
9
|
+
|
|
10
|
+
**Prerequisite:** a project is selected (`blocks use <tenantId>`, or pass `--project <tenantId>`) — see the blocks-onboarding skill. Every one of the four commands requires an impersonated project session; there is no account-level mode for any of them.
|
|
11
|
+
|
|
12
|
+
## Safe read commands
|
|
13
|
+
|
|
14
|
+
- **`blocks notification list [--page <n>] [--page-size <n>] [--sort-by <property>] [--sort-desc] [--filter <text>] [--json]`** — read-only.
|
|
15
|
+
- **`blocks notification get <itemId> [--json]`** — `itemId` may be given positionally or as `--id <itemId>`; one of the two is required (the command throws `Missing --id` if neither is given). Read-only.
|
|
16
|
+
|
|
17
|
+
Neither read command mutates anything and neither accepts `--dry-run` (there's nothing to preview — no request body is built, no confirmation gate exists for either).
|
|
18
|
+
|
|
19
|
+
## Mutating: save (create or update a channel config)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
blocks notification save --name <n> --channel <0|1> --type <0-3> --dry-run --json # preview first
|
|
23
|
+
blocks notification save --name <n> --channel <0|1> --type <0-3> --yes --json # only after approval
|
|
24
|
+
blocks notification save --update --body '<json>' --yes --json # full custom payload
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The request body is built by merging (in this order, later keys win) whatever `--body '<json>'` or `--file <path.json>` supplies, then these convenience flags layered on top (so an unset convenience flag never overwrites a value from `--body`/`--file`):
|
|
28
|
+
|
|
29
|
+
| Flag | Body field | Notes |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `--channel <int>` | `channelToNotify` | Raw integer — the CLI does **not** validate or enum-check the value itself. |
|
|
32
|
+
| `--type <int>` | `notificationType` | Same — raw integer, no validation in the command. |
|
|
33
|
+
| `--enable-persistence` | `enablePersistence` | Boolean flag; only ever sent as `true` when passed — passing it absent never sends an explicit `false`. |
|
|
34
|
+
| `--update` | `isUpdateRequest` | Same true-only pattern as `--enable-persistence`. Set this when saving over an existing config rather than creating a new one. |
|
|
35
|
+
| `--name <text>` | `name` | |
|
|
36
|
+
| `--notify-method <text>` | `notifyMethod` | |
|
|
37
|
+
|
|
38
|
+
`--dry-run` prints the resolved request and returns — no network call, no confirmation prompt. Without `--dry-run`, it prompts interactively for a typed `yes` before sending (unless `--yes` is also passed); only then does it resolve the selected project and issue the request. Always show `--dry-run` output and get explicit approval before re-running with `--yes`.
|
|
39
|
+
|
|
40
|
+
`--channel`/`--type` don't have machine-readable names published by the API; the CLI's own usage docs note they correspond to the OS API's channel/receiver-type enums (`--channel` roughly `0|1`, `--type` roughly `0-3`) — treat those ranges as documentation convention, not something this command enforces, and ask the user for the exact intended value rather than guessing one.
|
|
41
|
+
|
|
42
|
+
## Mutating: delete
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
blocks notification delete <itemId> --dry-run --json
|
|
46
|
+
blocks notification delete <itemId> --yes --json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Same `itemId` resolution as `get` — positional arg or `--id`, one required. `--dry-run` prints the resolved query and returns; otherwise it goes through the same confirmation gate as `save` (interactive `yes` prompt unless `--yes` is passed) before resolving the project and sending the request.
|
|
50
|
+
|
|
51
|
+
## Gotchas
|
|
52
|
+
|
|
53
|
+
- **No SDK path, ever.** If asked "how do I manage notification channels from my app," the answer is: you don't — this is CLI-only, human/CI-operated configuration, not something to wire into frontend or backend app code.
|
|
54
|
+
- **`notification` and `notifier` are not the same thing.** `notification save/list/get/delete` (this skill) configures *which channel/method* a notification type uses. `notifier notify/list/unread/mark-read/mark-all-read` *sends* notifications and reads a user's inbox — a separate command family with its own commands, not covered here. Don't answer a "send a notification" request with `notification save`, and don't answer a "configure the notification channel" request with `notifier`.
|
|
55
|
+
- **`list` and `get` have no `--dry-run`.** Only `save` and `delete` build a request that's worth previewing; the two read commands hit the API directly. Don't tell a user to `--dry-run` a `list` or `get` call.
|
|
56
|
+
- **`--enable-persistence` and `--update` are true-only flags.** Passing them sets the field to `true`; not passing them omits the field entirely (never sends an explicit `false`). If a user wants to explicitly *unset* persistence or force a plain create, that has to go through `--body`/`--file` directly, not the convenience flag.
|
|
57
|
+
- **`--channel`/`--type` are unvalidated raw integers.** The command will happily send any integer you give it — there's no local check against the underlying enums. Confirm the intended value with the user (or check the Blocks portal/API docs) rather than inventing one.
|
|
58
|
+
- **`itemId` for `get`/`delete` is always required**, positional or `--id` — never guessed. Ask the user rather than assuming a value.
|
|
59
|
+
- **Every command is project-scoped.** All four require a resolved project and an impersonated project token; behavior follows whichever project is currently selected via `blocks use` (or an explicit `--project` override).
|
|
60
|
+
- **`--dry-run` before `--yes`, always**, on `save` and `delete` — same discipline as every other mutating `blocks` command in this pack.
|
|
61
|
+
|
|
62
|
+
## Example trigger prompts
|
|
63
|
+
|
|
64
|
+
- "List the notification channel configs for this project."
|
|
65
|
+
- "Get notification config `<itemId>`."
|
|
66
|
+
- "Save a new notification config named `<name>` on channel 0, type 1." → preview with `--dry-run` first.
|
|
67
|
+
- "Update the existing `<name>` notification config." → add `--update`, still `--dry-run` before `--yes`.
|
|
68
|
+
- "Delete notification config `<itemId>`."
|
|
69
|
+
- "Send a notification to these users" / "show me a user's unread notifications" → not this skill; that's `blocks notifier *`, a different command family for sending/reading, not channel configuration.
|
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: blocks-notifier
|
|
3
|
-
description: "Push real-time/offline notifications and manage a signed-in user's own notification inbox, via both the SDK (`blocksClient.notifier.*`) and the CLI (`blocks notifier notify|list|unread|mark-read|mark-all-read`). Distinct from the sibling blocks-notification skill, which configures tenant notification-*channel* settings on a different backing service, not sending. `notifier unread` flattens its subscription filter into GET query params since Fetch forbids a GET body. `--dry-run` before `--yes` on CLI `notify`/`mark-read`/`mark-all-read`."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Blocks Notifier — Send & Inbox
|
|
7
|
-
|
|
8
|
-
`notifier` pushes real-time/offline notifications to users, roles, or subscription-filter matches, and reads/manages the signed-in user's own notification inbox. This is a **separate, deliberate concern from the blocks-notification skill**, which manages a tenant's notification-*channel configuration* — which channel/method a notification type uses — on an unrelated backing service. Both this skill and the sibling skill confirm the same distinction from their own side. Don't merge them, and don't reconcile them as if one were a typo for the other — they hit different backing services. If the ask is "configure which channel a notification type uses," route to the blocks-notification skill instead.
|
|
9
|
-
|
|
10
|
-
Unlike `blocks-notification` (100% CLI, no SDK path), **`notifier` has both a CLI and an SDK surface**, and every one of the five operations exists on both:
|
|
11
|
-
|
|
12
|
-
| Operation | CLI | SDK (`blocksClient.notifier.*`) |
|
|
13
|
-
|---|---|---|
|
|
14
|
-
| Send a notification | `blocks notifier notify` | `notify(request)` |
|
|
15
|
-
| List the inbox | `blocks notifier list` | `getNotifications(options)` |
|
|
16
|
-
| Unread by subscription filter | `blocks notifier unread` | `getUnreadNotificationsBySubscriptionFilter(request)` |
|
|
17
|
-
| Mark one read | `blocks notifier mark-read <id>` | `markNotificationAsRead(request)` |
|
|
18
|
-
| Mark all read | `blocks notifier mark-all-read` | `markAllNotificationAsRead()` |
|
|
19
|
-
|
|
20
|
-
## CLI — `blocks notifier *`
|
|
21
|
-
|
|
22
|
-
Every CLI command is project-scoped: each requires an impersonated project session — there is no account-level mode for any of the five. Project resolution follows the same order as everywhere else in this CLI: `--project <tenantId>` flag, then the workspace's `blocks.json`, then the globally selected project from `blocks use <tenantId>`. See the blocks-onboarding skill if no project is selected yet.
|
|
23
|
-
|
|
24
|
-
- **`blocks notifier notify [--user-ids a,b] [--roles a,b] [--connection-id <id>] [--configuration-name <n>] [--subscription-filters '<json>'] [--denormalized-payload <text>] [--save-denormalized-payload-as-object] [--content-available] [--response-key <k>] [--response-value <v>] [--body '<json>'|--file <path>] [--dry-run] [--yes] [--json]`** — target with at least one of `--user-ids`/`--roles`/`--subscription-filters`. `--user-ids` and `--roles` are comma-separated lists; `--subscription-filters` is a raw JSON array string (e.g. `[{"context":"orders","actionName":"created","value":"*"}]`, matching `BlocksNotifierSubscriptionFilter[]`). The body is built by merging `--body`/`--file` first, then layering the convenience flags on top — so an explicit convenience flag always wins over the same field in `--body`/`--file`, and an unset one never overwrites what `--body`/`--file` supplied. `--content-available` and `--save-denormalized-payload-as-object` are true-only booleans (absent when not passed, never an explicit `false`).
|
|
25
|
-
- **`blocks notifier list [--unread-only] [--page <n>] [--page-size <n>] [--sort-by <property>] [--sort-desc] [--filter <text>] [--json]`** — read-only.
|
|
26
|
-
- **`blocks notifier unread [--user-id <id>] [--context <c>] [--action-name <a>] [--value <v>] [--order-by <1|2>] [--json]`** — read-only. See "The GET-with-a-body quirk" below.
|
|
27
|
-
- **`blocks notifier mark-read <id> [--dry-run] [--yes] [--json]`** — `id` may be positional or `--id`; one of the two is required (`Missing --id` if neither given).
|
|
28
|
-
- **`blocks notifier mark-all-read [--dry-run] [--yes] [--json]`** — no body/arguments needed.
|
|
29
|
-
|
|
30
|
-
### The GET-with-a-body quirk (`unread`)
|
|
31
|
-
|
|
32
|
-
This operation is documented upstream as a read with a JSON request body, which the Fetch spec forbids sending on a plain GET-style read. Both the CLI and SDK work around it the same way: flatten the filter fields into the query string instead. Confirmed field names, read directly from source:
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
UserId
|
|
36
|
-
SubscriptionFilterData.Context
|
|
37
|
-
SubscriptionFilterData.ActionName
|
|
38
|
-
SubscriptionFilterData.Value
|
|
39
|
-
OrderBy
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
CLI flags map to them as `--user-id` -> `UserId`, `--context` -> `SubscriptionFilterData.Context`, `--action-name` -> `SubscriptionFilterData.ActionName`, `--value` -> `SubscriptionFilterData.Value`, `--order-by` -> `OrderBy` (an integer, `1|2` per the SDK type — its enum meaning isn't published anywhere, so treat it as opaque and confirm with the user rather than guessing which value means what).
|
|
43
|
-
|
|
44
|
-
This flattening is a **client-side inference, not something verified against a live call** — both the CLI and SDK made the same choice independently, which is corroborating but not proof the real backend accepts it. If a live `notifier unread` call ever errors, re-check this against the actual API response rather than assuming the flattening above is still correct.
|
|
45
|
-
|
|
46
|
-
## SDK — `blocksClient.notifier.*`
|
|
47
|
-
|
|
48
|
-
```ts
|
|
49
|
-
import { blocksClient } from "../../lib/blocks/client";
|
|
50
|
-
|
|
51
|
-
await blocksClient.notifier.notify({
|
|
52
|
-
userIds: ["user-1"],
|
|
53
|
-
roles: ["admin"],
|
|
54
|
-
denormalizedPayload: '{"orderId":"123"}',
|
|
55
|
-
saveDenormalizedPayloadAsAnObject: true
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const inbox = await blocksClient.notifier.getNotifications({ isUnreadOnly: true, page: 1, pageSize: 20 });
|
|
59
|
-
|
|
60
|
-
const unread = await blocksClient.notifier.getUnreadNotificationsBySubscriptionFilter({
|
|
61
|
-
userId: "user-1",
|
|
62
|
-
subscriptionFilterData: { context: "orders", actionName: "created" }
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
await blocksClient.notifier.markNotificationAsRead({ id: notificationId });
|
|
66
|
-
await blocksClient.notifier.markAllNotificationAsRead();
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
- **`notify(request: BlocksNotifyRequest)`** — fields: `configurationName?`, `connectionId?`, `contentAvailable?: boolean`, `denormalizedPayload?: string`, `responseKey?`, `responseValue?`, `roles?: string[]`, `saveDenormalizedPayloadAsAnObject?: boolean`, `subscriptionFilters?: BlocksNotifierSubscriptionFilter[]`, `userIds?: string[]`.
|
|
70
|
-
- **`getUnreadNotificationsBySubscriptionFilter(request)`** — fields: `orderBy?: 1 | 2` (opaque enum), `subscriptionFilterData?: { actionName?, context?, value? }`, `userId?`. Internally builds the same flattened query as the CLI (see above) — this is the SDK-side half of the same documented workaround.
|
|
71
|
-
- **`getNotifications(options: BlocksGetNotificationsOptions = {})`** — options: `filter?`, `isUnreadOnly?`, `page?`, `pageSize?`, `sortBy?`, `sortDescending?`. Response shape: `{ notifications: Record<string, unknown>[], totalNotificationsCount: number, unReadNotificationsCount: number }`.
|
|
72
|
-
- **`markAllNotificationAsRead()`** — no arguments.
|
|
73
|
-
- **`markNotificationAsRead(request: BlocksMarkNotificationAsReadRequest)`** — request: `{ id: string }`.
|
|
74
|
-
|
|
75
|
-
`BlocksOfflineNotification` (the shape of items `getUnreadNotificationsBySubscriptionFilter` resolves to) is a loose `Record<string, unknown>` plus known fields `correlationId?`, `createdTime?`, `denormalizedPayload?`, `id?`, `isRead?`, `payload?`, `readByRoles?: string[]`, `readByUserIds?: string[]`. `BlocksNotifierPassThroughResponse` (the `notify`/mark-read/mark-all-read return type) is an untyped `Record<string, unknown>` — the SDK doesn't shape it further; don't assume fields beyond what a live response actually contains.
|
|
76
|
-
|
|
77
|
-
The SDK methods don't take a project/tenant argument per call — project context comes from however the app's shared `blocksClient` instance was constructed (its `xBlocksKey`/`appDomain`), same as every other `blocksClient.*` namespace. Don't create a second client just for notifier calls.
|
|
78
|
-
|
|
79
|
-
## Mutation discipline (CLI only)
|
|
80
|
-
|
|
81
|
-
`notify`, `mark-read`, and `mark-all-read` are the three CLI mutations, and all three follow the same two-gate pattern used throughout this CLI:
|
|
82
|
-
|
|
83
|
-
1. **`--dry-run`** short-circuits before any network call or confirmation prompt, printing a preview of what would be sent (`mark-all-read`'s preview has no request body since it sends none).
|
|
84
|
-
2. Without `--dry-run`, a confirmation step accepts `--yes` outright or, interactively, prompts to type "yes" to continue, and cancels on anything else.
|
|
85
|
-
|
|
86
|
-
Always show the `--dry-run` output and get explicit approval before re-running with `--yes`. `list` and `unread` are read-only and have neither flag — don't tell a user to `--dry-run` a `list` or `unread` call. The SDK methods have no equivalent gate at all; that discipline is a CLI-only convention for terminal/CI operators, not something app code needs to replicate.
|
|
87
|
-
|
|
88
|
-
## Gotchas
|
|
89
|
-
|
|
90
|
-
- **`notifier` and `notification` are not the same thing, and this is not an oversight to fix.** `notifier` (this skill) sends notifications and reads a user's inbox, on both CLI and SDK. `notification` (the sibling skill) configures a tenant's notification channel settings, CLI-only. Don't answer a "send a notification" ask with `notification save`, and don't answer a "configure the channel" ask with `notifier`.
|
|
91
|
-
- **`notifier unread`'s query-param flattening is an inferred client-side workaround for a Fetch-spec conflict, not verified against a live call.** See "The GET-with-a-body quirk" above. If it ever errors in practice, re-check whether the real endpoint tolerates a body server-side (some non-browser/non-Node HTTP stacks do) before assuming the flattening itself is broken.
|
|
92
|
-
- **`--content-available` and `--save-denormalized-payload-as-object` on `notify` are true-only flags.** Passing them sends `true`; omitting them omits the field entirely — there's no way to send an explicit `false` through the convenience flags (use `--body`/`--file` for that).
|
|
93
|
-
- **Convenience flags on `notify` win over `--body`/`--file`.** The merge order is `--body`/`--file` first, then the individual flags spread on top — so a flag like `--connection-id` always overrides the same key in `--body` if both are given.
|
|
94
|
-
- **`order-by` (both CLI `--order-by` and SDK `orderBy`) is an opaque `1|2` enum** — its meaning isn't published anywhere. Ask the user or confirm from the live API rather than guessing which value sorts which way.
|
|
95
|
-
- **Every CLI command is project-scoped**; there's no account-level mode. The SDK has no per-call project argument — it inherits whatever project the shared `blocksClient` was configured for.
|
|
96
|
-
- **`mark-read`'s `id` (positional or `--id`) is always required** — never guessed or defaulted.
|
|
97
|
-
- **`--dry-run` before `--yes`, always**, on the three CLI mutations — same discipline as every other mutating `blocks` command in this pack.
|
|
98
|
-
|
|
99
|
-
## Example trigger prompts
|
|
100
|
-
|
|
101
|
-
- "Send a notification to these user IDs from my app." -> SDK `blocksClient.notifier.notify({ userIds: [...] })`.
|
|
102
|
-
- "Push a notification to everyone matching this subscription filter." -> SDK `notify({ subscriptionFilters: [...] })`, or `blocks notifier notify --subscription-filters '<json>' --dry-run --json` from the terminal.
|
|
103
|
-
- "Show me a user's unread notifications for the 'orders' context." -> `blocks notifier unread --user-id <id> --context orders --json`, or SDK `getUnreadNotificationsBySubscriptionFilter({ userId, subscriptionFilterData: { context: "orders" } })`.
|
|
104
|
-
- "List my notification inbox, unread only." -> `blocks notifier list --unread-only --json`, or SDK `getNotifications({ isUnreadOnly: true })`.
|
|
105
|
-
- "Mark this notification as read." -> `blocks notifier mark-read <id> --dry-run --json`, then `--yes`.
|
|
106
|
-
- "Mark everything in the inbox as read." -> `blocks notifier mark-all-read --dry-run --json`, then `--yes`.
|
|
107
|
-
- "Configure which channel the order-shipped notification uses." -> not this skill; that's the blocks-notification skill.
|
|
1
|
+
---
|
|
2
|
+
name: blocks-notifier
|
|
3
|
+
description: "Push real-time/offline notifications and manage a signed-in user's own notification inbox, via both the SDK (`blocksClient.notifier.*`) and the CLI (`blocks notifier notify|list|unread|mark-read|mark-all-read`). Distinct from the sibling blocks-notification skill, which configures tenant notification-*channel* settings on a different backing service, not sending. `notifier unread` flattens its subscription filter into GET query params since Fetch forbids a GET body. `--dry-run` before `--yes` on CLI `notify`/`mark-read`/`mark-all-read`."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Blocks Notifier — Send & Inbox
|
|
7
|
+
|
|
8
|
+
`notifier` pushes real-time/offline notifications to users, roles, or subscription-filter matches, and reads/manages the signed-in user's own notification inbox. This is a **separate, deliberate concern from the blocks-notification skill**, which manages a tenant's notification-*channel configuration* — which channel/method a notification type uses — on an unrelated backing service. Both this skill and the sibling skill confirm the same distinction from their own side. Don't merge them, and don't reconcile them as if one were a typo for the other — they hit different backing services. If the ask is "configure which channel a notification type uses," route to the blocks-notification skill instead.
|
|
9
|
+
|
|
10
|
+
Unlike `blocks-notification` (100% CLI, no SDK path), **`notifier` has both a CLI and an SDK surface**, and every one of the five operations exists on both:
|
|
11
|
+
|
|
12
|
+
| Operation | CLI | SDK (`blocksClient.notifier.*`) |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Send a notification | `blocks notifier notify` | `notify(request)` |
|
|
15
|
+
| List the inbox | `blocks notifier list` | `getNotifications(options)` |
|
|
16
|
+
| Unread by subscription filter | `blocks notifier unread` | `getUnreadNotificationsBySubscriptionFilter(request)` |
|
|
17
|
+
| Mark one read | `blocks notifier mark-read <id>` | `markNotificationAsRead(request)` |
|
|
18
|
+
| Mark all read | `blocks notifier mark-all-read` | `markAllNotificationAsRead()` |
|
|
19
|
+
|
|
20
|
+
## CLI — `blocks notifier *`
|
|
21
|
+
|
|
22
|
+
Every CLI command is project-scoped: each requires an impersonated project session — there is no account-level mode for any of the five. Project resolution follows the same order as everywhere else in this CLI: `--project <tenantId>` flag, then the workspace's `blocks.json`, then the globally selected project from `blocks use <tenantId>`. See the blocks-onboarding skill if no project is selected yet.
|
|
23
|
+
|
|
24
|
+
- **`blocks notifier notify [--user-ids a,b] [--roles a,b] [--connection-id <id>] [--configuration-name <n>] [--subscription-filters '<json>'] [--denormalized-payload <text>] [--save-denormalized-payload-as-object] [--content-available] [--response-key <k>] [--response-value <v>] [--body '<json>'|--file <path>] [--dry-run] [--yes] [--json]`** — target with at least one of `--user-ids`/`--roles`/`--subscription-filters`. `--user-ids` and `--roles` are comma-separated lists; `--subscription-filters` is a raw JSON array string (e.g. `[{"context":"orders","actionName":"created","value":"*"}]`, matching `BlocksNotifierSubscriptionFilter[]`). The body is built by merging `--body`/`--file` first, then layering the convenience flags on top — so an explicit convenience flag always wins over the same field in `--body`/`--file`, and an unset one never overwrites what `--body`/`--file` supplied. `--content-available` and `--save-denormalized-payload-as-object` are true-only booleans (absent when not passed, never an explicit `false`).
|
|
25
|
+
- **`blocks notifier list [--unread-only] [--page <n>] [--page-size <n>] [--sort-by <property>] [--sort-desc] [--filter <text>] [--json]`** — read-only.
|
|
26
|
+
- **`blocks notifier unread [--user-id <id>] [--context <c>] [--action-name <a>] [--value <v>] [--order-by <1|2>] [--json]`** — read-only. See "The GET-with-a-body quirk" below.
|
|
27
|
+
- **`blocks notifier mark-read <id> [--dry-run] [--yes] [--json]`** — `id` may be positional or `--id`; one of the two is required (`Missing --id` if neither given).
|
|
28
|
+
- **`blocks notifier mark-all-read [--dry-run] [--yes] [--json]`** — no body/arguments needed.
|
|
29
|
+
|
|
30
|
+
### The GET-with-a-body quirk (`unread`)
|
|
31
|
+
|
|
32
|
+
This operation is documented upstream as a read with a JSON request body, which the Fetch spec forbids sending on a plain GET-style read. Both the CLI and SDK work around it the same way: flatten the filter fields into the query string instead. Confirmed field names, read directly from source:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
UserId
|
|
36
|
+
SubscriptionFilterData.Context
|
|
37
|
+
SubscriptionFilterData.ActionName
|
|
38
|
+
SubscriptionFilterData.Value
|
|
39
|
+
OrderBy
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
CLI flags map to them as `--user-id` -> `UserId`, `--context` -> `SubscriptionFilterData.Context`, `--action-name` -> `SubscriptionFilterData.ActionName`, `--value` -> `SubscriptionFilterData.Value`, `--order-by` -> `OrderBy` (an integer, `1|2` per the SDK type — its enum meaning isn't published anywhere, so treat it as opaque and confirm with the user rather than guessing which value means what).
|
|
43
|
+
|
|
44
|
+
This flattening is a **client-side inference, not something verified against a live call** — both the CLI and SDK made the same choice independently, which is corroborating but not proof the real backend accepts it. If a live `notifier unread` call ever errors, re-check this against the actual API response rather than assuming the flattening above is still correct.
|
|
45
|
+
|
|
46
|
+
## SDK — `blocksClient.notifier.*`
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { blocksClient } from "../../lib/blocks/client";
|
|
50
|
+
|
|
51
|
+
await blocksClient.notifier.notify({
|
|
52
|
+
userIds: ["user-1"],
|
|
53
|
+
roles: ["admin"],
|
|
54
|
+
denormalizedPayload: '{"orderId":"123"}',
|
|
55
|
+
saveDenormalizedPayloadAsAnObject: true
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const inbox = await blocksClient.notifier.getNotifications({ isUnreadOnly: true, page: 1, pageSize: 20 });
|
|
59
|
+
|
|
60
|
+
const unread = await blocksClient.notifier.getUnreadNotificationsBySubscriptionFilter({
|
|
61
|
+
userId: "user-1",
|
|
62
|
+
subscriptionFilterData: { context: "orders", actionName: "created" }
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await blocksClient.notifier.markNotificationAsRead({ id: notificationId });
|
|
66
|
+
await blocksClient.notifier.markAllNotificationAsRead();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **`notify(request: BlocksNotifyRequest)`** — fields: `configurationName?`, `connectionId?`, `contentAvailable?: boolean`, `denormalizedPayload?: string`, `responseKey?`, `responseValue?`, `roles?: string[]`, `saveDenormalizedPayloadAsAnObject?: boolean`, `subscriptionFilters?: BlocksNotifierSubscriptionFilter[]`, `userIds?: string[]`.
|
|
70
|
+
- **`getUnreadNotificationsBySubscriptionFilter(request)`** — fields: `orderBy?: 1 | 2` (opaque enum), `subscriptionFilterData?: { actionName?, context?, value? }`, `userId?`. Internally builds the same flattened query as the CLI (see above) — this is the SDK-side half of the same documented workaround.
|
|
71
|
+
- **`getNotifications(options: BlocksGetNotificationsOptions = {})`** — options: `filter?`, `isUnreadOnly?`, `page?`, `pageSize?`, `sortBy?`, `sortDescending?`. Response shape: `{ notifications: Record<string, unknown>[], totalNotificationsCount: number, unReadNotificationsCount: number }`.
|
|
72
|
+
- **`markAllNotificationAsRead()`** — no arguments.
|
|
73
|
+
- **`markNotificationAsRead(request: BlocksMarkNotificationAsReadRequest)`** — request: `{ id: string }`.
|
|
74
|
+
|
|
75
|
+
`BlocksOfflineNotification` (the shape of items `getUnreadNotificationsBySubscriptionFilter` resolves to) is a loose `Record<string, unknown>` plus known fields `correlationId?`, `createdTime?`, `denormalizedPayload?`, `id?`, `isRead?`, `payload?`, `readByRoles?: string[]`, `readByUserIds?: string[]`. `BlocksNotifierPassThroughResponse` (the `notify`/mark-read/mark-all-read return type) is an untyped `Record<string, unknown>` — the SDK doesn't shape it further; don't assume fields beyond what a live response actually contains.
|
|
76
|
+
|
|
77
|
+
The SDK methods don't take a project/tenant argument per call — project context comes from however the app's shared `blocksClient` instance was constructed (its `xBlocksKey`/`appDomain`), same as every other `blocksClient.*` namespace. Don't create a second client just for notifier calls.
|
|
78
|
+
|
|
79
|
+
## Mutation discipline (CLI only)
|
|
80
|
+
|
|
81
|
+
`notify`, `mark-read`, and `mark-all-read` are the three CLI mutations, and all three follow the same two-gate pattern used throughout this CLI:
|
|
82
|
+
|
|
83
|
+
1. **`--dry-run`** short-circuits before any network call or confirmation prompt, printing a preview of what would be sent (`mark-all-read`'s preview has no request body since it sends none).
|
|
84
|
+
2. Without `--dry-run`, a confirmation step accepts `--yes` outright or, interactively, prompts to type "yes" to continue, and cancels on anything else.
|
|
85
|
+
|
|
86
|
+
Always show the `--dry-run` output and get explicit approval before re-running with `--yes`. `list` and `unread` are read-only and have neither flag — don't tell a user to `--dry-run` a `list` or `unread` call. The SDK methods have no equivalent gate at all; that discipline is a CLI-only convention for terminal/CI operators, not something app code needs to replicate.
|
|
87
|
+
|
|
88
|
+
## Gotchas
|
|
89
|
+
|
|
90
|
+
- **`notifier` and `notification` are not the same thing, and this is not an oversight to fix.** `notifier` (this skill) sends notifications and reads a user's inbox, on both CLI and SDK. `notification` (the sibling skill) configures a tenant's notification channel settings, CLI-only. Don't answer a "send a notification" ask with `notification save`, and don't answer a "configure the channel" ask with `notifier`.
|
|
91
|
+
- **`notifier unread`'s query-param flattening is an inferred client-side workaround for a Fetch-spec conflict, not verified against a live call.** See "The GET-with-a-body quirk" above. If it ever errors in practice, re-check whether the real endpoint tolerates a body server-side (some non-browser/non-Node HTTP stacks do) before assuming the flattening itself is broken.
|
|
92
|
+
- **`--content-available` and `--save-denormalized-payload-as-object` on `notify` are true-only flags.** Passing them sends `true`; omitting them omits the field entirely — there's no way to send an explicit `false` through the convenience flags (use `--body`/`--file` for that).
|
|
93
|
+
- **Convenience flags on `notify` win over `--body`/`--file`.** The merge order is `--body`/`--file` first, then the individual flags spread on top — so a flag like `--connection-id` always overrides the same key in `--body` if both are given.
|
|
94
|
+
- **`order-by` (both CLI `--order-by` and SDK `orderBy`) is an opaque `1|2` enum** — its meaning isn't published anywhere. Ask the user or confirm from the live API rather than guessing which value sorts which way.
|
|
95
|
+
- **Every CLI command is project-scoped**; there's no account-level mode. The SDK has no per-call project argument — it inherits whatever project the shared `blocksClient` was configured for.
|
|
96
|
+
- **`mark-read`'s `id` (positional or `--id`) is always required** — never guessed or defaulted.
|
|
97
|
+
- **`--dry-run` before `--yes`, always**, on the three CLI mutations — same discipline as every other mutating `blocks` command in this pack.
|
|
98
|
+
|
|
99
|
+
## Example trigger prompts
|
|
100
|
+
|
|
101
|
+
- "Send a notification to these user IDs from my app." -> SDK `blocksClient.notifier.notify({ userIds: [...] })`.
|
|
102
|
+
- "Push a notification to everyone matching this subscription filter." -> SDK `notify({ subscriptionFilters: [...] })`, or `blocks notifier notify --subscription-filters '<json>' --dry-run --json` from the terminal.
|
|
103
|
+
- "Show me a user's unread notifications for the 'orders' context." -> `blocks notifier unread --user-id <id> --context orders --json`, or SDK `getUnreadNotificationsBySubscriptionFilter({ userId, subscriptionFilterData: { context: "orders" } })`.
|
|
104
|
+
- "List my notification inbox, unread only." -> `blocks notifier list --unread-only --json`, or SDK `getNotifications({ isUnreadOnly: true })`.
|
|
105
|
+
- "Mark this notification as read." -> `blocks notifier mark-read <id> --dry-run --json`, then `--yes`.
|
|
106
|
+
- "Mark everything in the inbox as read." -> `blocks notifier mark-all-read --dry-run --json`, then `--yes`.
|
|
107
|
+
- "Configure which channel the order-shipped notification uses." -> not this skill; that's the blocks-notification skill.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: blocks-onboarding
|
|
3
|
-
description: "Onboard a user into SELISE Blocks before any other Blocks skill can run, using the `blocks` CLI — never raw API calls. Detects
|
|
3
|
+
description: "Onboard a user into SELISE Blocks before any other Blocks skill can run, using the `blocks` CLI — never raw API calls. Detects CLI/login/project state via `blocks auth status --json`/`doctor --json`, closes install/login/project gaps, resolves the app OIDC client, scaffolds with `blocks new web`, then runs `blocks init` only inside the generated/existing app directory when local Blocks files are needed. Use for new users or `not_logged_in`/`project_not_selected`."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Blocks — Onboarding
|
|
@@ -61,12 +61,11 @@ blocks use <x-blocks-key> # reselect the same x-blocks-key to force a fresh
|
|
|
61
61
|
|
|
62
62
|
## Step 3 — Local workspace + hand off
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
- `blocks
|
|
69
|
-
- `blocks new web <name> --x-blocks-key <tenantId> --app-domain <domain> --client-id <the-resolved-client-id>`. **Always pass `--client-id` and `--app-domain` explicitly** — omitting either drops `new web` into an interactive pick-list prompt with no non-interactive escape (not even to "skip"), which hangs a scripted/agent run with no stdin to answer it. Omit `--blocks-api-url` unless the project uses a non-default gateway; the scaffold derives it from the app domain, e.g. `https://dqrsf.slsblx.com` -> `https://blocksapi.slsblx.com`.
|
|
64
|
+
Route to what the user actually wants. Do **not** run `blocks init` from a parent workspace before scaffolding a new app; it creates `blocks.json` and `blocks/` in the current directory. For a new frontend, scaffold first, `cd <appName>`, then run `blocks init` there only when the work needs project-local Blocks files such as data schemas or rules. For an existing app, run `blocks init` from that app's root. Safe to re-run: it never overwrites files that already exist. (`init` does not create a localization folder or any release-related file — `blocks/localization/` only appears later, lazily, the first time `blocks localization pull` writes to it, and there is no `blocks/release/*` file at all.)
|
|
65
|
+
|
|
66
|
+
- Building a frontend from scratch → resolve the app's public OIDC client first, then scaffold:
|
|
67
|
+
- `blocks auth oidc-clients list --json` — check whether a client already registered for this project fits. If none fits, create one directly (no portal visit needed): `blocks auth oidc-clients save --client-display-name <appName> --client-type public --redirect-uris https://<domain>:5173/login/callback --scope "openid profile" --require-pkce --register-as-identity-provider --auto-redirect --dry-run --json`, then re-run with `--yes` after showing the dry-run output and getting approval. `--client-type public` is required — IAM derives `tokenEndpointAuthMethod` from it, so omitting it stores a browser client as confidential. `--register-as-identity-provider` creates the linked identity provider in the same call; nothing further to run. `--auto-redirect` matters too — the scaffolded login page already navigates straight to the provider itself, so without it IAM's hosted login page shows a redundant manual "continue" click. When updating an *existing* client instead of creating one, always pass `--item-id` — the save endpoint replaces the whole client document, and the CLI fetches the current one first to merge your change into it rather than resetting the rest. See the blocks-iam-sso-oidc-configuration skill for the full decision tree and field-level gotchas.
|
|
68
|
+
- `blocks new web <name> --x-blocks-key <tenantId> --app-domain <domain> --client-id <the-resolved-client-id>`. **Always pass `--client-id` and `--app-domain` explicitly** — omitting either drops `new web` into an interactive pick-list prompt with no non-interactive escape (not even to "skip"), which hangs a scripted/agent run with no stdin to answer it. Omit `--blocks-api-url` unless the project uses a non-default gateway; the scaffold derives it from the app domain, e.g. `https://dqrsf.slsblx.com` -> `https://blocksapi.slsblx.com`. After scaffolding, `cd <name>` before installing packages, running `blocks init`, or adding local skill files so `blocks.json` and `blocks/` stay inside the app. Once it resolves the client id, `new web` also checks the tenant's AuthController config and turns on `isOidcEnabled` if it's off — nothing further to do for login to actually work; if you're wiring an existing app instead (`blocks sdk client`, no `new web` call), check that yourself first: `blocks auth config get --json`, and if `isOidcEnabled` is `false`, `blocks auth config save --oidc-enabled --dry-run --json` then `--yes`.
|
|
70
69
|
- Defining data / CRUD / localization / release on an existing project → hand off to the matching skill; the project is already selected via `blocks use`, so its commands can proceed directly.
|
|
71
70
|
|
|
72
71
|
## Gotchas
|