@seliseblocks/cli-os 0.2.10 → 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.
- package/AI_USAGE_GUIDE.md +11 -20
- package/LICENSE +21 -21
- package/README.md +3 -5
- package/bin/run.js +2 -2
- package/dist/commands/auth/oidc-clients/save.js +4 -1
- package/dist/commands/auth/refresh.js +21 -2
- package/dist/commands/mfa/generate.js +7 -4
- package/dist/commands/mfa/method-set.js +13 -4
- package/dist/commands/mfa/totp-enable.d.ts +3 -3
- package/dist/commands/mfa/totp-enable.js +4 -4
- package/dist/commands/mfa/verify.js +4 -2
- package/dist/commands/new/web.js +3 -2
- package/dist/commands/projects/create.js +110 -16
- package/dist/index.js +34 -40
- package/dist/lib/oidc-discovery.d.ts +2 -0
- package/dist/lib/oidc-discovery.js +12 -0
- package/package.json +47 -47
- package/dist/commands/sdk/client.d.ts +0 -1
- package/dist/commands/sdk/client.js +0 -99
- package/dist/commands/skill/add.d.ts +0 -1
- package/dist/commands/skill/add.js +0 -19
- package/dist/commands/skill/list.d.ts +0 -1
- package/dist/commands/skill/list.js +0 -15
- package/dist/commands/skill/show.d.ts +0 -1
- package/dist/commands/skill/show.js +0 -15
- package/dist/lib/skills.d.ts +0 -17
- package/dist/lib/skills.js +0 -69
- package/dist/skills/blocks-data-gateway-configuration/SKILL.md +0 -204
- package/dist/skills/blocks-data-gateway-crud/SKILL.md +0 -223
- package/dist/skills/blocks-data-storage/SKILL.md +0 -253
- package/dist/skills/blocks-data-storage/flows/object-management.md +0 -124
- package/dist/skills/blocks-frontend-local-https/SKILL.md +0 -100
- package/dist/skills/blocks-iam-access-control/SKILL.md +0 -49
- package/dist/skills/blocks-iam-access-control/flows/feature-gating.md +0 -38
- package/dist/skills/blocks-iam-access-control/flows/manage-roles-permissions.md +0 -110
- package/dist/skills/blocks-iam-account/SKILL.md +0 -169
- package/dist/skills/blocks-iam-mfa/SKILL.md +0 -124
- package/dist/skills/blocks-iam-organizations/SKILL.md +0 -43
- package/dist/skills/blocks-iam-organizations/flows/admin-mutations.md +0 -89
- package/dist/skills/blocks-iam-organizations/flows/read-and-switch.md +0 -57
- package/dist/skills/blocks-iam-sso-oidc-configuration/SKILL.md +0 -105
- package/dist/skills/blocks-iam-sso-oidc-implementation/SKILL.md +0 -80
- package/dist/skills/blocks-iam-users/SKILL.md +0 -131
- package/dist/skills/blocks-localization-configuration/SKILL.md +0 -149
- package/dist/skills/blocks-localization-implementation/SKILL.md +0 -63
- package/dist/skills/blocks-mail/SKILL.md +0 -95
- package/dist/skills/blocks-notification/SKILL.md +0 -69
- package/dist/skills/blocks-notifier/SKILL.md +0 -107
- package/dist/skills/blocks-onboarding/SKILL.md +0 -77
- package/dist/skills/blocks-release-deployment/SKILL.md +0 -81
- package/dist/skills/blocks-secrets/SKILL.md +0 -81
- package/dist/skills/blocks-storage-configuration/SKILL.md +0 -93
- package/dist/skills/lint.mjs +0 -168
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: blocks-data-storage
|
|
3
|
-
description: "Build file and document-management features on SELISE Blocks Data: upload/download, directory trees, cursor-paginated browsing and search, file versions, rename/move/copy, soft delete/trash/restore, sharing, access policies, and inheritance. Use for attachments, file browsers, folders, shared files, permissions, or version history. Use 'blocks data files *' for terminal/admin work and @seliseblocks/client data.files/data.directories/data.objects for app code."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Blocks Data — Storage
|
|
7
|
-
|
|
8
|
-
Treat storage as one permission-aware object tree containing **directories** and **files**. Uploading a new file now creates the file object in that tree; there is no second DMS registration step.
|
|
9
|
-
|
|
10
|
-
Use:
|
|
11
|
-
|
|
12
|
-
- **`blocks data files *`** for supported terminal/admin operations.
|
|
13
|
-
- **`@seliseblocks/client`** for app code. Use the shared `blocksClient` created by blocks-onboarding.
|
|
14
|
-
- **blocks-storage-configuration** only to manage the named provider configuration used by `configurationName`.
|
|
15
|
-
|
|
16
|
-
Select a project first with `blocks use <tenantId>`. If login/project state or the shared client setup is unknown, use blocks-onboarding before this skill.
|
|
17
|
-
|
|
18
|
-
Store the returned `fileId` in a Data record when attaching a file to domain data. Use blocks-data-gateway-crud for the record mutation.
|
|
19
|
-
|
|
20
|
-
## Choose the surface
|
|
21
|
-
|
|
22
|
-
The current CLI and SDK follow the backend's file, directory, and object resource groups.
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
blocks --version
|
|
26
|
-
blocks data files --help
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
- Use `blocks data files *` for terminal/admin work and inspect `blocks --help` for exact flags.
|
|
30
|
-
- Use `blocksClient.data.files` for bytes, metadata, versions, and file operations.
|
|
31
|
-
- Use `blocksClient.data.directories` for directory create/get/update/delete/move.
|
|
32
|
-
- Use `blocksClient.data.objects` for browse/search/trash/shared/restore/share/access/inheritance.
|
|
33
|
-
|
|
34
|
-
Legacy `data.dms.*`, `dms-upload`, `dms-list`, `create-folder`, and `delete-folder` wrappers are retired. Uploads need no registration call.
|
|
35
|
-
|
|
36
|
-
## Upload a new file
|
|
37
|
-
|
|
38
|
-
Choose one path from the project's storage configuration.
|
|
39
|
-
|
|
40
|
-
| Provider category | Sequence |
|
|
41
|
-
|---|---|
|
|
42
|
-
| Cloud object storage | request a pre-signed URL, then PUT the bytes to it |
|
|
43
|
-
| Local/SFTP storage | send one authenticated multipart upload |
|
|
44
|
-
|
|
45
|
-
Both paths create the file object and initial version directly. Do not call `dms-upload` afterward.
|
|
46
|
-
|
|
47
|
-
### Cloud upload from the CLI
|
|
48
|
-
|
|
49
|
-
Prefer the composed command. It previews and confirms both metadata creation and the provider PUT:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
blocks data files upload --file ./invoice.pdf --parent-id <directoryId> \
|
|
53
|
-
--configuration-name Default --access-modifier Private --dry-run --json
|
|
54
|
-
blocks data files upload --file ./invoice.pdf --parent-id <directoryId> \
|
|
55
|
-
--configuration-name Default --access-modifier Private --yes --json
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Use the two explicit commands only when the intermediate upload URL is required:
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
blocks data files presigned-upload-url \
|
|
62
|
-
--name invoice.pdf \
|
|
63
|
-
--parent-directory-id <directoryId> \
|
|
64
|
-
--configuration-name Default \
|
|
65
|
-
--access-modifier Private \
|
|
66
|
-
--dry-run --json
|
|
67
|
-
blocks data files presigned-upload-url \
|
|
68
|
-
--name invoice.pdf \
|
|
69
|
-
--parent-directory-id <directoryId> \
|
|
70
|
-
--configuration-name Default \
|
|
71
|
-
--access-modifier Private \
|
|
72
|
-
--yes --json
|
|
73
|
-
|
|
74
|
-
blocks data files upload-to-url \
|
|
75
|
-
--url "<uploadUrl>" \
|
|
76
|
-
--file ./invoice.pdf \
|
|
77
|
-
--content-type application/pdf \
|
|
78
|
-
--dry-run --json
|
|
79
|
-
blocks data files upload-to-url \
|
|
80
|
-
--url "<uploadUrl>" \
|
|
81
|
-
--file ./invoice.pdf \
|
|
82
|
-
--content-type application/pdf \
|
|
83
|
-
--yes --json
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
The presign response contains `uploadUrl`, `fileId`, and `isSuccess`. The first call creates the file metadata/version; the PUT fills its object-storage key. Handle PUT failure explicitly because it can leave metadata for missing bytes.
|
|
87
|
-
|
|
88
|
-
When `parentDirectoryId` is empty, the cloud upload resolves `moduleName` to that module's default directory. The backend default is module value `8` (`Default_Construct`), but pass the intended module or a concrete directory id instead of relying on that default.
|
|
89
|
-
|
|
90
|
-
### Cloud upload from app code
|
|
91
|
-
|
|
92
|
-
```ts
|
|
93
|
-
const presign = await blocksClient.data.files.presignedUploadUrl({
|
|
94
|
-
name: "invoice.pdf",
|
|
95
|
-
parentDirectoryId: directoryId,
|
|
96
|
-
configurationName: "Default",
|
|
97
|
-
accessModifier: "Private",
|
|
98
|
-
tags: "invoice,2026",
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
if (!presign || typeof presign !== "object") {
|
|
102
|
-
throw new Error("Unexpected upload response");
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const result = presign as {
|
|
106
|
-
uploadUrl: string;
|
|
107
|
-
fileId: string;
|
|
108
|
-
isSuccess: boolean;
|
|
109
|
-
errors?: Record<string, string>;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
if (!result.isSuccess) throw new Error(JSON.stringify(result.errors));
|
|
113
|
-
|
|
114
|
-
await blocksClient.data.files.uploadToUrl({
|
|
115
|
-
url: result.uploadUrl,
|
|
116
|
-
body: file,
|
|
117
|
-
contentType: file.type || "application/octet-stream",
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
`uploadToUrl` is provider-direct and sends no bearer token or `x-blocks-key`. The SDK adds Azure's `x-ms-blob-type: Blockblob` header unless overridden; ensure that header matches the signed provider policy.
|
|
122
|
-
|
|
123
|
-
### Local-storage upload
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
blocks data files upload-to-local-storage \
|
|
127
|
-
--file ./invoice.pdf \
|
|
128
|
-
--parent-directory-id <directoryId> \
|
|
129
|
-
--configuration-name Default \
|
|
130
|
-
--access-modifier Private \
|
|
131
|
-
--dry-run --json
|
|
132
|
-
blocks data files upload-to-local-storage \
|
|
133
|
-
--file ./invoice.pdf \
|
|
134
|
-
--parent-directory-id <directoryId> \
|
|
135
|
-
--configuration-name Default \
|
|
136
|
-
--access-modifier Private \
|
|
137
|
-
--yes --json
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
```ts
|
|
141
|
-
const uploaded = await blocksClient.data.files.uploadToLocalStorage({
|
|
142
|
-
name: file.name,
|
|
143
|
-
file,
|
|
144
|
-
parentDirectoryId: directoryId,
|
|
145
|
-
configurationName: "Default",
|
|
146
|
-
accessModifier: "Private",
|
|
147
|
-
tags: ["invoice", "2026"],
|
|
148
|
-
});
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
This call creates the file object and uploads version 1 in one request. Unlike cloud presign, an empty local `parentDirectoryId` stays at the top level; it is not resolved through `moduleName`.
|
|
152
|
-
|
|
153
|
-
## Add a file version
|
|
154
|
-
|
|
155
|
-
Supplying an existing `itemId` to either upload flow creates another version after the caller passes the file's Edit check. For cloud storage, the dedicated command/method returns another pre-signed URL:
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
blocks data files versions <fileId> --limit 25 --json
|
|
159
|
-
blocks data files create-version <fileId> --configuration-name Default --dry-run --json
|
|
160
|
-
blocks data files create-version <fileId> --configuration-name Default --yes --json
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
const history = await blocksClient.data.files.versions({ fileId, limit: 25 });
|
|
165
|
-
const next = await blocksClient.data.files.createVersion({ fileId, configurationName: "Default" });
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Version history is newest-first and cursor-paginated. Use the returned opaque `nextCursor`; the current backend uses the last version number internally, but callers must not construct cursors. Limits are 1–100, default 25.
|
|
169
|
-
|
|
170
|
-
## Read and download
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
blocks data files get <fileId> --configuration-name Default --json
|
|
174
|
-
blocks data files get <fileId> --version <versionNo> --configuration-name Default --json
|
|
175
|
-
blocks data files get-many <fileId...> --configuration-name Default --json
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
```ts
|
|
179
|
-
const file = await blocksClient.data.files.get(fileId, {
|
|
180
|
-
configurationName: "Default",
|
|
181
|
-
version: 2,
|
|
182
|
-
});
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
The response includes a download URL plus metadata. Download requires the caller's Download permission. Access-denied reads may deliberately look like missing resources so clients cannot probe hidden object ids.
|
|
186
|
-
|
|
187
|
-
## Directory and object workflows
|
|
188
|
-
|
|
189
|
-
For browsing, directories, search, move/copy/rename, trash, sharing, and ACL behavior, read [flows/object-management.md](flows/object-management.md).
|
|
190
|
-
|
|
191
|
-
## Update custom metadata
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
blocks data files update-additional-info <fileId> \
|
|
195
|
-
--additional-properties '{"status":"reviewed"}' \
|
|
196
|
-
--dry-run --json
|
|
197
|
-
blocks data files update-additional-info <fileId> \
|
|
198
|
-
--additional-properties '{"status":"reviewed"}' \
|
|
199
|
-
--yes --json
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
This updates `additionalProperties`; it does not rename, move, tag, or version the file.
|
|
203
|
-
|
|
204
|
-
## Delete safely
|
|
205
|
-
|
|
206
|
-
Deletion now distinguishes trash from permanent removal:
|
|
207
|
-
|
|
208
|
-
- `permanent: false` archives the file or directory so it can be restored.
|
|
209
|
-
- `permanent: true` removes it for good.
|
|
210
|
-
- The backend default is **`true`** when `permanent` is omitted.
|
|
211
|
-
|
|
212
|
-
The CLI defaults to safe soft deletion. Add `--permanent` only after explicit approval.
|
|
213
|
-
|
|
214
|
-
```bash
|
|
215
|
-
blocks data files delete <fileId> --dry-run --json
|
|
216
|
-
blocks data files delete <fileId> --yes --json
|
|
217
|
-
blocks data files delete <fileId> --permanent --dry-run --json
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
In app code, send the choice explicitly: `blocksClient.data.files.delete({ fileId, permanent: false })`.
|
|
221
|
-
|
|
222
|
-
## Permission model
|
|
223
|
-
|
|
224
|
-
Every storage request passes two checks:
|
|
225
|
-
|
|
226
|
-
1. The endpoint permission permits that class of action.
|
|
227
|
-
2. The object ACL permits the action on that specific file/directory.
|
|
228
|
-
|
|
229
|
-
Capabilities are ordered: `View`, `Download`, `Edit`, `Delete`, `Manage`, `Owner`. Higher capabilities imply lower ones. Directory children inherit ancestor access while `inheritsParentAccess` is true. New files inherit from their directory.
|
|
230
|
-
|
|
231
|
-
Do not infer permission from a visible button or endpoint grant. Render actions from each object's returned `permissions` flags and still handle 403/404 races.
|
|
232
|
-
|
|
233
|
-
## Gotchas
|
|
234
|
-
|
|
235
|
-
- Upload now creates the visible file object; legacy DMS registration is wrong and may fail after the bytes were successfully PUT.
|
|
236
|
-
- Cloud presign creates metadata before the provider PUT. Treat the two steps as a recoverable workflow and surface partial failure.
|
|
237
|
-
- A name must contain an allowed extension. Directories may restrict extensions.
|
|
238
|
-
- Names are unique within a directory. File move/copy can fail on a name conflict or extension policy.
|
|
239
|
-
- `Private` is the safe default. Use `Public` only when unauthenticated download is intended.
|
|
240
|
-
- `configurationName` selects an existing provider record; it does not configure storage.
|
|
241
|
-
- Most legacy file SDK methods return `Promise<unknown>`; validate responses at the boundary.
|
|
242
|
-
- Never send Blocks auth headers to a pre-signed provider URL.
|
|
243
|
-
- Never use a raw API call to work around a missing CLI/SDK wrapper; update the client surface first.
|
|
244
|
-
|
|
245
|
-
## Example triggers
|
|
246
|
-
|
|
247
|
-
- "Upload this PDF into the Contracts folder."
|
|
248
|
-
- "Build a file browser with folders and search."
|
|
249
|
-
- "Show files shared with the current user."
|
|
250
|
-
- "Move this file to trash and let users restore it."
|
|
251
|
-
- "Add version history and upload a replacement version."
|
|
252
|
-
- "Share this directory with a role and let its children inherit access."
|
|
253
|
-
- "Move, copy, or rename a file."
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# Object management
|
|
2
|
-
|
|
3
|
-
Use `blocks data files *` from a terminal and `blocksClient.data.directories` / `blocksClient.data.objects` / `blocksClient.data.files` in app code. Do not fall back to legacy `data.dms.*` helpers or raw HTTP.
|
|
4
|
-
|
|
5
|
-
## Browse a directory
|
|
6
|
-
|
|
7
|
-
Use the current `get-objects` operation with:
|
|
8
|
-
|
|
9
|
-
- `parentDirectoryId` for a concrete directory, or `moduleName` to resolve a module's default directory when no parent id is supplied.
|
|
10
|
-
- `cursor` from the previous response and `limit` from 1–200 (default 50).
|
|
11
|
-
- `type: "directory" | "file"` to narrow results.
|
|
12
|
-
- `search` for name filtering within that parent.
|
|
13
|
-
|
|
14
|
-
The result contains `items`, `nextCursor`, `hasMore`, and `totalChildCount`. Each item has a lowercase `type` discriminator and permission flags. Continue while `hasMore` using `nextCursor`; access filtering can produce a short page even when more results remain.
|
|
15
|
-
|
|
16
|
-
Use `search-objects` for a case-insensitive name search across descendants. Pass `query`, optional `directoryId`, optional type, cursor, and limit. Search text is treated literally rather than as a regular expression.
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
blocks data files list --parent-id <directoryId> --type file --limit 50 --json
|
|
20
|
-
blocks data files search invoice --directory-id <directoryId> --json
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
const page = await blocksClient.data.objects.list({ parentDirectoryId, limit: 50 });
|
|
25
|
-
const matches = await blocksClient.data.objects.search({ query: "invoice", directoryId });
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Manage directories
|
|
29
|
-
|
|
30
|
-
- **Create:** provide `name`, an existing `parentDirectoryId`, optional `description`, `configurationName`, and `allowedFileExtensions`. If the parent is empty but `moduleName` is present, the backend resolves the module default. Creating a true root uses a separate owner-only capability and should not be a normal app action.
|
|
31
|
-
- **Read details:** get one directory by `directoryId`. The response includes path, ancestors, child counts, size, extension rules, inheritance, timestamps, and permissions.
|
|
32
|
-
- **Update:** provide `directoryId` and optional `name`/`description`. Default directories cannot be renamed.
|
|
33
|
-
- **Move:** provide `directoryId` and `targetDirectoryId`; an empty target means top level. Moving into self/descendants is rejected. Default directories cannot move.
|
|
34
|
-
- **Delete:** send `permanent: false` for trash. Permanent deletion requires an empty directory. Default directories cannot be deleted.
|
|
35
|
-
|
|
36
|
-
Directory names must be 1–255 characters, trimmed, not `.`/`..`, and contain no slash or backslash. Descriptions are limited to 2,000 characters.
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
blocks data files directory-create Contracts --parent-id <directoryId> \
|
|
40
|
-
--allowed-extensions pdf,docx --dry-run --json
|
|
41
|
-
blocks data files directory-get <directoryId> --json
|
|
42
|
-
blocks data files directory-update <directoryId> --name Agreements --dry-run --json
|
|
43
|
-
blocks data files directory-move <directoryId> --target-directory-id <targetId> --dry-run --json
|
|
44
|
-
blocks data files directory-delete <directoryId> --dry-run --json
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Use the matching `blocksClient.data.directories.create/get/update/move/delete` methods in app code.
|
|
48
|
-
|
|
49
|
-
## Manage files
|
|
50
|
-
|
|
51
|
-
- **Rename:** needs Edit and a unique name in the current directory.
|
|
52
|
-
- **Move:** needs Delete on the source file and Edit on the target directory. Stored bytes and versions stay in place.
|
|
53
|
-
- **Copy:** needs View on the source and Edit on the target. The new file gets a new id; version rows reference the same immutable stored bytes. It inherits from the target. Set `copyAccessPolicies` only when the user intends to duplicate direct policy entries.
|
|
54
|
-
- **Versions:** list newest-first with a cursor and limit 1–100. Creating a cloud version returns `versionNo` and `uploadUrl`; PUT the bytes to that URL without Blocks auth headers.
|
|
55
|
-
|
|
56
|
-
Move/copy reject a target name collision and a file extension disallowed by the target directory.
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
blocks data files rename <fileId> --name final.pdf --dry-run --json
|
|
60
|
-
blocks data files move <fileId> --target-directory-id <targetId> --dry-run --json
|
|
61
|
-
blocks data files copy <fileId> --target-directory-id <targetId> --dry-run --json
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Use `blocksClient.data.files.rename`, `.move`, and `.copy` in app code.
|
|
65
|
-
|
|
66
|
-
## Trash and restore
|
|
67
|
-
|
|
68
|
-
Use soft delete (`permanent: false`) to archive an item. List archived files/directories with `get-trash`, optionally filtering by type and paging with cursor/limit. Restore with `restore-from-trash`; permanently purge an archived item with `delete-from-trash` only after explicit approval.
|
|
69
|
-
|
|
70
|
-
Restore returns the item to its original parent. Reads and mutations remain ACL-filtered while the item is archived.
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
blocks data files trash --type file --json
|
|
74
|
-
blocks data files restore <resourceId> --dry-run --json
|
|
75
|
-
blocks data files purge <resourceId> --dry-run --json
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Use `blocksClient.data.objects.trash`, `.restore`, and `.deleteFromTrash` in app code.
|
|
79
|
-
|
|
80
|
-
## Shared objects
|
|
81
|
-
|
|
82
|
-
Use `get-shared-objects` for live items shared with the caller. It is cursor-paginated and may be filtered by `directory`/`file`. Owned objects are excluded. Direct or inherited allow entries for the current user, role, or organization qualify as shares.
|
|
83
|
-
|
|
84
|
-
Use `share-object` for the common allow-only action. Provide:
|
|
85
|
-
|
|
86
|
-
- `resourceId`
|
|
87
|
-
- `resourceType`: `Directory` or `File`
|
|
88
|
-
- `principalType`: `User`, `Role`, `Organization`, or `Everyone`
|
|
89
|
-
- `principalId` for every type except `Everyone`
|
|
90
|
-
- `permission`: `View`, `Download`, `Edit`, `Delete`, `Manage`, or `Owner`
|
|
91
|
-
- optional future `expiresAt`
|
|
92
|
-
|
|
93
|
-
Sharing requires Manage on the resource.
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
blocks data files shared --json
|
|
97
|
-
blocks data files share <resourceId> --resource-type Directory \
|
|
98
|
-
--principal-type Role --principal-id editors --permission Edit --dry-run --json
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Use `blocksClient.data.objects.shared` and `.share` in app code.
|
|
102
|
-
|
|
103
|
-
## Advanced access policies
|
|
104
|
-
|
|
105
|
-
Use the policy operations only for an access-management UI:
|
|
106
|
-
|
|
107
|
-
- `get-access-policies` lists direct entries on a resource.
|
|
108
|
-
- `grant-access` adds Allow or Deny with optional priority and expiry.
|
|
109
|
-
- `update-access-policy` replaces an entry and requires its `policyItemId`.
|
|
110
|
-
- `revoke-access-policy` removes an entry by `resourceId` and `policyItemId`.
|
|
111
|
-
- `resolve-access` returns the caller's `canView`, `canDownload`, `canEdit`, `canDelete`, `canManage`, and `canOwner` flags.
|
|
112
|
-
- `toggle-inheritance` changes whether ancestors participate in resolution.
|
|
113
|
-
|
|
114
|
-
Enums are JSON strings, not guessed numeric values. Priority must be non-negative and expiry must be in the future.
|
|
115
|
-
|
|
116
|
-
Policy guardrails:
|
|
117
|
-
|
|
118
|
-
- Manage is required to change access.
|
|
119
|
-
- A Deny aimed at the resource owner is rejected.
|
|
120
|
-
- Turning inheritance off is rejected until the resource has a direct Allow entry, preventing an orphaned resource.
|
|
121
|
-
- The request model for access-policy listing contains `includeInherited`, but the current controller does not use it; do not promise inherited entries in that response. Use `resolve-access` for effective permissions.
|
|
122
|
-
- Object reads may return 404 instead of 403 to avoid revealing hidden ids.
|
|
123
|
-
|
|
124
|
-
CLI access commands are `access-list`, `access-grant`, `access-update`, `access-revoke`, `access-resolve`, and `inheritance`. Their SDK equivalents are under `blocksClient.data.objects`.
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: blocks-frontend-local-https
|
|
3
|
-
description: "Run a scaffolded (`blocks new web`) Blocks app locally over HTTPS on its real project domain — required for hosted IAM login, since plain HTTP or localhost never gets the session cookie. The scaffold already automates cert generation (npm run cert, no OpenSSL needed) and HTTPS serving via vite.config.ts. Covers running that flow, trusting the cert, the hosts-file entry, and troubleshooting. Use when running a scaffolded app over HTTPS, hitting 'SSO cookie not set' / Vite 'Blocked request' errors, trusting the dev cert, or asking why local login redirects back but doesn't stay signed in."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Blocks Frontend — Local HTTPS for a Scaffolded App
|
|
7
|
-
|
|
8
|
-
This is the local dev loop for an app already created with `blocks new web` (see the blocks-onboarding skill for the scaffold command itself — this skill doesn't repeat it). The scaffold **already generates its own cert tooling**; nothing here is built from scratch, and nothing uses raw `openssl`/`curl`/`fetch`.
|
|
9
|
-
|
|
10
|
-
## Why localhost doesn't work
|
|
11
|
-
|
|
12
|
-
Browser login goes through the hosted Blocks IAM IdP flow (`blocksClient.auth.idp.redirectToProvider()` / `.callback()`), which finishes by IAM setting a **Secure, domain-scoped session cookie**. Browsers won't store or send that cookie on plain `http://localhost` — it has to be HTTPS, and it has to be on the project's real domain, or the cookie silently never lands and the app looks "logged in" for a second then bounces back to logged-out.
|
|
13
|
-
|
|
14
|
-
## Where the domain comes from
|
|
15
|
-
|
|
16
|
-
`--app-domain` on `blocks new web` is the app's real Blocks origin, e.g. `https://dbpdba.seliseblocks.com`. The generated `.env` keeps two derived values:
|
|
17
|
-
|
|
18
|
-
- `VITE_BLOCKS_APP_DOMAIN` — the full value as passed, with scheme (`https://dbpdba.seliseblocks.com`).
|
|
19
|
-
- `VITE_BLOCKS_DEV_HOST` — the same host with no scheme (`dbpdba.seliseblocks.com`), computed by the scaffold generator and used everywhere locally: hosts file, cert `commonName`/SAN, and `vite.config.ts`'s `server.host`/`allowedHosts`.
|
|
20
|
-
|
|
21
|
-
You never need to look this up separately — it's already sitting in `.env` after scaffolding.
|
|
22
|
-
|
|
23
|
-
## The flow
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
cd <appName>
|
|
27
|
-
npm install
|
|
28
|
-
npm run cert
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
`npm run cert` runs `scripts/generate-cert.mjs`, which reads `VITE_BLOCKS_DEV_HOST` from `.env` (or `process.env`, or an explicit `npm run cert -- <domain>` argument) and uses the `selfsigned` npm dependency to write `.cert/dev-key.pem` and `.cert/dev-cert.pem`, with `subjectAltName` covering the domain, `localhost`, and `127.0.0.1`. This is pure Node — it works from a plain PowerShell prompt with no OpenSSL and no Git Bash/WSL switch.
|
|
32
|
-
|
|
33
|
-
Next, map the domain to your machine — add one line to the hosts file (needs admin/elevated):
|
|
34
|
-
|
|
35
|
-
```text
|
|
36
|
-
127.0.0.1 dbpdba.seliseblocks.com
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Windows path: `C:\Windows\System32\drivers\etc\hosts` (edit as Administrator).
|
|
40
|
-
|
|
41
|
-
Then:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm run dev
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
`vite.config.ts` picks up `.cert/dev-key.pem` + `.cert/dev-cert.pem` automatically when both exist and serves HTTPS on `VITE_BLOCKS_DEV_HOST`:`VITE_BLOCKS_DEV_PORT` (default port `5173`, `strictPort: true` because the port is baked into the registered OIDC redirect URI). It also sets `allowedHosts` to that domain, working around Vite's default DNS-rebinding protection which otherwise 404s custom hosts with "Blocked request."
|
|
48
|
-
|
|
49
|
-
Finally, open the app at:
|
|
50
|
-
|
|
51
|
-
```text
|
|
52
|
-
https://dbpdba.seliseblocks.com:5173
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**Not** `http://`, **not** `localhost` — either one skips the cookie entirely even though the app loads.
|
|
56
|
-
|
|
57
|
-
## Trusting the cert (optional but recommended)
|
|
58
|
-
|
|
59
|
-
The cert is self-signed, so the browser shows a one-time warning until trusted. `npm run cert` prints the exact trust command for your OS when it finishes:
|
|
60
|
-
|
|
61
|
-
- Windows (elevated prompt): `certutil -addstore -f Root .cert\dev-cert.pem`
|
|
62
|
-
- macOS: `sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain .cert/dev-cert.pem`
|
|
63
|
-
- Linux: `sudo cp .cert/dev-cert.pem /usr/local/share/ca-certificates/blocks-dev.crt && sudo update-ca-certificates`
|
|
64
|
-
|
|
65
|
-
Restart the browser after trusting so it picks up the new trust store entry. `.cert/` is already gitignored by the scaffold — it's per-machine, never committed.
|
|
66
|
-
|
|
67
|
-
## Still need a public OIDC client
|
|
68
|
-
|
|
69
|
-
Local HTTPS alone doesn't make login succeed if no OIDC client is registered yet, or if its `redirect_uris` don't include this exact dev origin. That registration is portal-only (see blocks-onboarding's Gotchas) — out of scope here, but it's the next thing to check if HTTPS is right and login still fails. The redirect URI must match byte-for-byte, including `:5173`.
|
|
70
|
-
|
|
71
|
-
## Gotchas
|
|
72
|
-
|
|
73
|
-
**Custom app domain: the API base URL must share its registrable domain, or the cookie never lands.** `blocks new web --app-domain` isn't limited to `*.seliseblocks.com` — it also supports custom domains (e.g. `abc.slsblx.com`, `xyz.blx10.com`). On a custom domain, the hosted-login session cookie is only stored by the browser if `VITE_BLOCKS_API_URL` shares the app's registrable domain. Concretely:
|
|
74
|
-
|
|
75
|
-
- `abc.slsblx.com` → `VITE_BLOCKS_API_URL` must be `https://blocksapi.slsblx.com`
|
|
76
|
-
- `xyz.blx10.com` → `VITE_BLOCKS_API_URL` must be `https://blocksapi.blx10.com`
|
|
77
|
-
|
|
78
|
-
If `VITE_BLOCKS_API_URL` is left at the default `https://api.seliseblocks.com` while the app itself runs on a custom domain, the browser treats the API as cross-site relative to the app and never stores the cookie — login still redirects back and *looks* successful, but cookie-based calls (`/iam/me`, organization switching, logout) silently fail. Check `VITE_BLOCKS_API_URL` in `.env` first whenever the app domain is not `*.seliseblocks.com` and auth-dependent calls are failing despite HTTPS and the cert being set up correctly.
|
|
79
|
-
|
|
80
|
-
## Troubleshooting
|
|
81
|
-
|
|
82
|
-
| Symptom | Likely cause | Fix |
|
|
83
|
-
|---|---|---|
|
|
84
|
-
| Login redirects back but app shows logged-out | Opened on `http://` or `localhost` instead of the HTTPS dev host | Check the URL bar: must be `https://<VITE_BLOCKS_DEV_HOST>:5173` exactly |
|
|
85
|
-
| Browser "not private" warning | Self-signed cert not trusted yet | Run the OS trust command `npm run cert` printed, then restart the browser |
|
|
86
|
-
| Vite: "Blocked request. This host is not allowed" | `allowedHosts` doesn't include the host being requested — usually `.env`'s `VITE_BLOCKS_DEV_HOST` was edited/missing after scaffold, or the browser is hitting a different hostname than expected | Confirm `.env` has the right `VITE_BLOCKS_DEV_HOST` and that the URL bar matches it exactly |
|
|
87
|
-
| Page doesn't load at all / DNS error | Hosts file entry missing or wrong | Add `127.0.0.1 <VITE_BLOCKS_DEV_HOST>` to `C:\Windows\System32\drivers\etc\hosts` (as Administrator) |
|
|
88
|
-
| `npm run cert` errors "No domain given" | `.env` missing or `VITE_BLOCKS_DEV_HOST` not set | Confirm `.env` exists with `VITE_BLOCKS_DEV_HOST=<host>`, or run `npm run cert -- <domain>` explicitly |
|
|
89
|
-
| `npm run dev` fails to bind the port | Port 5173 already in use (dev server uses `strictPort`, won't fall back) | Free port 5173, or the port is fixed because it's part of the registered OIDC redirect URI — don't just change it without updating the OIDC client too |
|
|
90
|
-
| HTTPS works, cert trusted, login still fails | No OIDC client registered yet, or its redirect URI doesn't match this origin exactly | Register/update the public OIDC client in the portal with `https://<VITE_BLOCKS_DEV_HOST>:5173/login/callback` as a redirect URI (portal-only, see blocks-onboarding) |
|
|
91
|
-
| Works for one dev, fails for a teammate | Each machine needs its own cert + hosts entry — `.cert/` is gitignored on purpose | Teammate runs `npm run cert` and adds the hosts entry on their own machine |
|
|
92
|
-
|
|
93
|
-
## Example trigger prompts
|
|
94
|
-
|
|
95
|
-
- "Run my app locally over HTTPS on its real domain so SSO works"
|
|
96
|
-
- "My local login isn't working — it just bounces back to the login page"
|
|
97
|
-
- "How do I set up the dev cert for this scaffolded app?"
|
|
98
|
-
- "I'm getting 'Blocked request. This host is not allowed' from Vite"
|
|
99
|
-
- "Do I need OpenSSL to run npm run cert on Windows?"
|
|
100
|
-
- "Why does login work in production but not on localhost?"
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: blocks-iam-access-control
|
|
3
|
-
description: "Work with SELISE Blocks RBAC (roles & permissions) via `blocks iam roles/permissions *` (CLI, project-scoped) or `blocksClient.iam.*` (SDK), never raw fetch/curl. Two facets: read-only feature-gating by the current user's own roles/permissions (common, safe) vs. creating/editing role and permission definitions (sensitive, human-confirmed only — CLI `--dry-run`→`--yes` or an in-app admin screen). OIDC/identity-provider provisioning stays portal-only, a different concern. Use for permission-gated UI, role/permission pickers, or building/scripting role & permission admin ('gate this button by permission', 'create a role and grant permissions', 'list permissions by severity')."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Blocks IAM — Access Control (Permissions & Roles)
|
|
7
|
-
|
|
8
|
-
This skill covers **permission and role definitions** in SELISE Blocks — the RBAC model itself, not who has which role (that's the blocks-iam-users skill). Everything goes through either `blocks iam roles/permissions *` (CLI) or `blocksClient.iam.*` from **`@seliseblocks/client`**, the single SDK instance every `blocks new web` scaffold wires up at `src/lib/blocks/client.ts` and exports as `blocksClient`. No raw `fetch`/`curl` for either surface.
|
|
9
|
-
|
|
10
|
-
```ts
|
|
11
|
-
import { blocksClient } from "../../lib/blocks/client";
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## The platform boundary — read this before writing any code
|
|
15
|
-
|
|
16
|
-
Role and permission administration is **not** portal-only or app-UI-only — `blocks` has a full, working CLI surface for it too. There are two equally real surfaces for the same operations, and the choice is about *where the human is*, not which one is "allowed" — see [flows/manage-roles-permissions.md](flows/manage-roles-permissions.md) for the full command reference and the CLI-vs-SDK decision.
|
|
17
|
-
|
|
18
|
-
Identity-provider/OIDC client provisioning is the one piece that really is **portal-only, human-driven**, at `https://os.seliseblocks.com` — unrelated to roles/permissions, don't bolt it onto this skill.
|
|
19
|
-
|
|
20
|
-
Keep the two facets below (read-only feature-gating vs. sensitive admin mutations) separate in your head (and in your code) — they have very different risk profiles regardless of which surface (CLI or SDK) you're using.
|
|
21
|
-
|
|
22
|
-
## Facet 1 — Feature-gating a frontend by the user's own permissions (common, low risk)
|
|
23
|
-
|
|
24
|
-
Read-only, scoped to whoever is signed in, needs no special confirmation. `useCurrentUser()` + `iam.resources.features()` + `iam.roles.assignable()`.
|
|
25
|
-
|
|
26
|
-
→ Full walkthrough: [flows/feature-gating.md](flows/feature-gating.md)
|
|
27
|
-
|
|
28
|
-
## Facet 2 — Creating/editing roles & permissions (sensitive)
|
|
29
|
-
|
|
30
|
-
Legitimate only in direct response to a human's explicit, in-the-moment instruction — CLI (`--dry-run` reviewed, then `--yes`) or a signed-in admin's own in-app screen. Never something an agent decides to invoke on its own initiative.
|
|
31
|
-
|
|
32
|
-
→ Full command reference, SDK methods, and confirm-before-mutating pattern: [flows/manage-roles-permissions.md](flows/manage-roles-permissions.md)
|
|
33
|
-
|
|
34
|
-
## Gotchas
|
|
35
|
-
|
|
36
|
-
- **CLI mutations are project-scoped, not account-scoped** — they run against the impersonated-project token; `blocks iam me` is the one IAM command that uses the account token instead.
|
|
37
|
-
- **Role hierarchy and permission assignment key off `slug`**, not `itemId`.
|
|
38
|
-
- **Never fire a create/update/assign-permissions call — CLI or SDK — without a human confirming that specific change first.** See [flows/manage-roles-permissions.md](flows/manage-roles-permissions.md) for the full discipline.
|
|
39
|
-
- **OIDC/identity-provider client provisioning is always portal-only**, independent of everything above.
|
|
40
|
-
|
|
41
|
-
## Example trigger prompts
|
|
42
|
-
|
|
43
|
-
- "Only show the delete button to users who have the `order::delete` permission." → Facet 1
|
|
44
|
-
- "Hide this whole admin section unless the signed-in user has an admin role." → Facet 1
|
|
45
|
-
- "What roles am I allowed to assign to other users?" → Facet 1
|
|
46
|
-
- "Show me permissions grouped by severity in a settings panel." → Facet 1
|
|
47
|
-
- "Build an admin page where I can create a role and pick which permissions it gets." → Facet 2
|
|
48
|
-
- "Create a `content-editor` role from the CLI with these permissions." → Facet 2
|
|
49
|
-
- "Can you just set up a few default roles for my project?" → confirm the exact list with the human first (in chat, or via a reviewed `--dry-run`), then run each `blocks iam roles create`/`assign-permissions` with `--yes` only after they say go — don't auto-provision without that per-change confirmation.
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Flow: Feature-gating a frontend by the user's own permissions (common, low risk)
|
|
2
|
-
|
|
3
|
-
This is read-only against IAM and scoped to whoever is signed in, so it needs no special confirmation — build it the same way you'd build any other data-fetching feature.
|
|
4
|
-
|
|
5
|
-
The scaffold already gives you a `useCurrentUser()` hook (`src/features/profile/useCurrentUser.ts`) wrapping `blocksClient.iam.me()` with TanStack Query; `me()` returns `{ data: { itemId, email, firstName, lastName, roles: string[], permissions: string[], ... } }`. Reuse it instead of re-fetching:
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
// src/features/access/usePermission.ts
|
|
9
|
-
import { useCurrentUser } from "../profile/useCurrentUser";
|
|
10
|
-
|
|
11
|
-
export function useHasPermission(permission: string): boolean {
|
|
12
|
-
const me = useCurrentUser();
|
|
13
|
-
return me.data?.data?.permissions?.includes(permission) ?? false;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function useHasRole(role: string): boolean {
|
|
17
|
-
const me = useCurrentUser();
|
|
18
|
-
return me.data?.data?.roles?.includes(role) ?? false;
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
```tsx
|
|
23
|
-
// src/shared/ui/RequirePermission.tsx
|
|
24
|
-
import type { ReactNode } from "react";
|
|
25
|
-
import { useHasPermission } from "../../features/access/usePermission";
|
|
26
|
-
|
|
27
|
-
export function RequirePermission({ permission, children }: { permission: string; children: ReactNode }) {
|
|
28
|
-
if (!useHasPermission(permission)) return null;
|
|
29
|
-
return <>{children}</>;
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Two more read methods round this out:
|
|
34
|
-
|
|
35
|
-
- `blocksClient.iam.resources.features(query?)` — feature/resource flags for the active user context; use this to drive nav items or feature flags that are more granular than a flat permission string.
|
|
36
|
-
- `blocksClient.iam.roles.assignable()` — lists roles the **current caller** is allowed to assign. If you're building a "grant this user a role" picker, populate it from `assignable()`, not from `roles.list()` — don't assume every role in the system is one this particular admin may hand out.
|
|
37
|
-
|
|
38
|
-
There is also a CLI read path for the same data, useful outside an app (scripting/inspection): `blocks iam roles list/get/assignable` and `blocks iam permissions list/get/by-severity` — see [manage-roles-permissions.md](manage-roles-permissions.md) for the full CLI command reference (it covers both reads and mutations).
|