@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,93 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: blocks-storage-configuration
|
|
3
|
-
description: "Configure which storage provider (Azure Blob, S3-compatible object storage, or local/SFTP storage) backs a SELISE Blocks project's file object tree: named configurations with host, port, credentials, region/endpoint or connection string, and strategy, via the blocks CLI ('storage config get/list/save/delete'). CLI-only, project-scoped admin surface. Use to create, inspect, rotate, switch, or delete provider configurations; file/directory/object operations belong to blocks-data-storage."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Blocks Storage — Configuration
|
|
7
|
-
|
|
8
|
-
This skill manages the **storage configuration record itself** — which cloud provider (or local/SFTP storage) a named configuration points at, and the connection details needed to reach it. It does not upload, download, browse, share, version, move, or trash objects; those runtime concerns belong to blocks-data-storage.
|
|
9
|
-
|
|
10
|
-
**CLI-only, no SDK path.** There is no `@seliseblocks/client` method for reading or writing a storage configuration's own fields. Runtime storage calls select an existing record by `configurationName`. If the user wants to manipulate a file/directory or its access policies, hand off to blocks-data-storage.
|
|
11
|
-
|
|
12
|
-
**Prerequisite:** a project is selected (`blocks use <tenantId>`). If login/project state is unknown, run the blocks-onboarding skill first.
|
|
13
|
-
|
|
14
|
-
## Command family
|
|
15
|
-
|
|
16
|
-
All four commands require an **impersonated project token** — there is no account-token path for this surface, consistent with other project-scoped admin commands (`secrets *`, `data config *`, etc.).
|
|
17
|
-
|
|
18
|
-
| Command | Notes |
|
|
19
|
-
|---|---|
|
|
20
|
-
| `blocks storage config list` | No parameters beyond the selected project. Read-only. |
|
|
21
|
-
| `blocks storage config get <name>` | `<name>` (positional) or `--name` (required if no positional arg). Read-only. |
|
|
22
|
-
| `blocks storage config save` | Upsert — create or update a configuration. Mutating. |
|
|
23
|
-
| `blocks storage config delete <name>` | `<name>` (positional) or `--name` (required if no positional arg). Mutating. |
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
blocks storage config list --json
|
|
27
|
-
blocks storage config get Default --json
|
|
28
|
-
blocks storage config get --name Default --json
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## `storage config save` — fields
|
|
32
|
-
|
|
33
|
-
`save` builds its request body from `--body`/`--file` (a raw JSON object, spread first) merged with these convenience flags (later, so they win if both are given):
|
|
34
|
-
|
|
35
|
-
| Flag | Body field |
|
|
36
|
-
|---|---|
|
|
37
|
-
| `--name` | `name` |
|
|
38
|
-
| `--item-id` | `itemId` |
|
|
39
|
-
| `--strategy` | `storageStrategy` |
|
|
40
|
-
| `--host` | `host` |
|
|
41
|
-
| `--port` | `port` |
|
|
42
|
-
| `--region-endpoint` | `cloudStorageRegionEndPoint` |
|
|
43
|
-
| `--connection-string` | `connectionString` |
|
|
44
|
-
| `--access-key` | `accessKey` |
|
|
45
|
-
| `--secret-key` | `secretKey` |
|
|
46
|
-
| `--username` | `userName` |
|
|
47
|
-
| `--password` | `password` |
|
|
48
|
-
| `--remote-base-path` | `remoteBasePath` |
|
|
49
|
-
| `--update` (boolean) | `updateRequest` |
|
|
50
|
-
|
|
51
|
-
Unset flags are dropped (`compact`), so they never overwrite fields already present in a `--body`/`--file` payload. `save` is a create-or-update in one command, not two separate verbs — pass `--item-id` (and typically `--update`) when modifying an existing configuration, omit it to create a new one.
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
blocks storage config save --name Default --strategy AzureBlob \
|
|
55
|
-
--host mystorageaccount.blob.core.windows.net --region-endpoint eu-west-1 \
|
|
56
|
-
--access-key <key> --secret-key <secret> --dry-run --json
|
|
57
|
-
blocks storage config save --name Default --strategy AzureBlob \
|
|
58
|
-
--host mystorageaccount.blob.core.windows.net --region-endpoint eu-west-1 \
|
|
59
|
-
--access-key <key> --secret-key <secret> --yes --json
|
|
60
|
-
|
|
61
|
-
# Update an existing configuration
|
|
62
|
-
blocks storage config save --item-id <id> --update --connection-string "<new connection string>" --dry-run --json
|
|
63
|
-
blocks storage config save --item-id <id> --update --connection-string "<new connection string>" --yes --json
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## `--dry-run` before `--yes` — always
|
|
67
|
-
|
|
68
|
-
Both mutating commands (`save`, `delete`) follow the standard `blocks` mutation discipline: `--dry-run` prints what would be sent and returns without calling the API; `--yes` skips the interactive confirmation prompt and sends the request for real. Omitting both drops into an interactive "Type 'yes' to continue" prompt — not viable in a scripted/agent context, so always pass one or the other explicitly.
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
blocks storage config delete Default --dry-run --json
|
|
72
|
-
blocks storage config delete Default --yes --json
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
`save`'s dry-run output redacts secret-shaped fields before printing (`accessKey`, `connectionString`, `password`, `secretKey` become `"***"`) — this redaction is **dry-run-preview only**, it does not change what's actually sent when you run with `--yes`, and it doesn't apply to `get`/`list` responses (see Gotchas).
|
|
76
|
-
|
|
77
|
-
## Gotchas
|
|
78
|
-
|
|
79
|
-
- **`get`/`list` are not redacted.** Only `save --dry-run`'s own preview output redacts `accessKey`/`connectionString`/`password`/`secretKey`. If a `get`/`list` response ever echoes credential fields back, treat that output as sensitive — don't paste it into logs, tickets, or chat verbatim.
|
|
80
|
-
- **`save` is upsert, not separate create/update commands.** Whether a call creates or updates is determined by whether `--item-id` is present, not by a different command name.
|
|
81
|
-
- **This is provider configuration, not object management.** `blocks storage config *` never touches file bytes, directory hierarchy, versions, trash, sharing, or ACLs. Those belong to **blocks-data-storage**, using a `configurationName` that a storage config already defines.
|
|
82
|
-
- **No positional-or-flag ambiguity trap:** `get`/`delete` accept the configuration name as either the first positional argument or `--name`; only one is required, not both.
|
|
83
|
-
- **Impersonated project token only.** Like `secrets *` and `data config *`, none of these four commands run against the account token — a project must be selected first (`blocks use <tenantId>`).
|
|
84
|
-
|
|
85
|
-
## Example trigger prompts
|
|
86
|
-
|
|
87
|
-
- "Set up Azure Blob storage for this project." → `storage config save --strategy AzureBlob ...`.
|
|
88
|
-
- "What storage configurations exist on this project?" → `storage config list`.
|
|
89
|
-
- "Show me the `Default` storage configuration." → `storage config get Default`.
|
|
90
|
-
- "Rotate the access key on our storage config." → `storage config save --item-id <id> --update --access-key <new key> ...`.
|
|
91
|
-
- "Switch this project to local storage." → `storage config save --strategy <local strategy value> --host ... --port ...` (confirm the exact strategy value expected by the project rather than guessing).
|
|
92
|
-
- "Delete this storage configuration, we don't use it anymore." → `storage config delete <name>`.
|
|
93
|
-
- "How do I actually upload a file once storage is configured?" → hand off to **blocks-data-storage**, not this skill.
|
package/dist/skills/lint.mjs
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Consistency lint for blocks-skills/. Run: node blocks-skills/lint.mjs
|
|
3
|
-
//
|
|
4
|
-
// A skill is consumed by an AI that has ONLY the globally-installed `blocks`
|
|
5
|
-
// CLI and a project-local `@seliseblocks/client` -- never this monorepo, and
|
|
6
|
-
// `blocks skill add` pulls exactly one skill directory at a time. Checks:
|
|
7
|
-
// 1. Every skill directory has a SKILL.md with frontmatter: `name` matches the
|
|
8
|
-
// directory name, `name` <= 64 chars, `description` present, non-empty,
|
|
9
|
-
// on a single physical line (blocks-cli's own frontmatter parser --
|
|
10
|
-
// src/lib/skills.ts -- is a hand-rolled line-by-line parser with no YAML
|
|
11
|
-
// dependency; a description that wraps onto a second line silently breaks
|
|
12
|
-
// `blocks skill list`/`show`), and <= 1024 chars (hard fail) / <= 700 chars
|
|
13
|
-
// (warn -- this pack's house style target is ~400-600).
|
|
14
|
-
// 2. Relative markdown links (in SKILL.md and any flows/*.md) resolve to a
|
|
15
|
-
// real file.
|
|
16
|
-
// 3. No links leave the containing skill's own directory at all -- not into
|
|
17
|
-
// another skill's SKILL.md, not into its flows/, not to a monorepo-only
|
|
18
|
-
// file outside blocks-skills/. A skill may mention another skill BY NAME
|
|
19
|
-
// in plain text, never as a link, since the target isn't guaranteed to be
|
|
20
|
-
// present for a consumer who only pulled this one skill. Links within the
|
|
21
|
-
// same skill's own directory (SKILL.md <-> its own flows/*.md) are fine.
|
|
22
|
-
// 4. No raw API endpoint paths (e.g. `/iam/v4/...`, `/os/v4/...`) -- skills
|
|
23
|
-
// describe CLI commands and SDK methods, never the wire protocol behind
|
|
24
|
-
// them; citing a path is exactly the kind of detail that could tempt a
|
|
25
|
-
// raw fetch/curl bypass every skill already forbids.
|
|
26
|
-
// Exit 0 = clean, 1 = problems found (all listed, not just the first).
|
|
27
|
-
import { readdirSync, readFileSync, existsSync, statSync } from "node:fs";
|
|
28
|
-
import { dirname, join, relative } from "node:path";
|
|
29
|
-
import { fileURLToPath } from "node:url";
|
|
30
|
-
|
|
31
|
-
const skillsDir = dirname(fileURLToPath(import.meta.url));
|
|
32
|
-
const errors = [];
|
|
33
|
-
const warnings = [];
|
|
34
|
-
|
|
35
|
-
const DESCRIPTION_HARD_LIMIT = 1024;
|
|
36
|
-
const DESCRIPTION_WARN_LIMIT = 700;
|
|
37
|
-
const NAME_LIMIT = 64;
|
|
38
|
-
const ENDPOINT_PATTERN = /\/(iam|data|os|logic|release|localization)\/v4\/[A-Za-z0-9/{}._-]*/g;
|
|
39
|
-
|
|
40
|
-
const skillDirs = readdirSync(skillsDir, { withFileTypes: true })
|
|
41
|
-
.filter((entry) => entry.isDirectory())
|
|
42
|
-
.map((entry) => entry.name)
|
|
43
|
-
.sort();
|
|
44
|
-
|
|
45
|
-
for (const skillName of skillDirs) {
|
|
46
|
-
const skillPath = join(skillsDir, skillName);
|
|
47
|
-
const skillMdPath = join(skillPath, "SKILL.md");
|
|
48
|
-
|
|
49
|
-
if (!existsSync(skillMdPath)) {
|
|
50
|
-
errors.push(`${skillName}/: no SKILL.md`);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
checkFrontmatter(skillName, skillMdPath);
|
|
55
|
-
checkLinksInFile(skillName, skillMdPath);
|
|
56
|
-
checkEndpointsInFile(skillMdPath);
|
|
57
|
-
|
|
58
|
-
const flowsDir = join(skillPath, "flows");
|
|
59
|
-
if (existsSync(flowsDir) && statSync(flowsDir).isDirectory()) {
|
|
60
|
-
for (const entry of readdirSync(flowsDir, { withFileTypes: true })) {
|
|
61
|
-
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
62
|
-
const flowPath = join(flowsDir, entry.name);
|
|
63
|
-
checkLinksInFile(skillName, flowPath);
|
|
64
|
-
checkEndpointsInFile(flowPath);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function checkFrontmatter(skillName, skillMdPath) {
|
|
71
|
-
const raw = readFileSync(skillMdPath, "utf8");
|
|
72
|
-
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
73
|
-
const rel = relative(skillsDir, skillMdPath);
|
|
74
|
-
|
|
75
|
-
if (!match) {
|
|
76
|
-
errors.push(`${rel}: missing frontmatter (expected a leading --- ... --- block)`);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const lines = match[1].split(/\r?\n/);
|
|
81
|
-
const nameLine = lines.find((line) => line.startsWith("name:"));
|
|
82
|
-
const descLine = lines.find((line) => line.startsWith("description:"));
|
|
83
|
-
|
|
84
|
-
if (!nameLine) {
|
|
85
|
-
errors.push(`${rel}: frontmatter has no 'name' field`);
|
|
86
|
-
} else {
|
|
87
|
-
const name = nameLine.slice("name:".length).trim();
|
|
88
|
-
if (name !== skillName) {
|
|
89
|
-
errors.push(`${rel}: name '${name}' does not match directory name '${skillName}'`);
|
|
90
|
-
}
|
|
91
|
-
if (name.length > NAME_LIMIT) {
|
|
92
|
-
errors.push(`${rel}: name is ${name.length} chars, over the ${NAME_LIMIT}-char limit`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!descLine) {
|
|
97
|
-
errors.push(`${rel}: frontmatter has no 'description' field`);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const singleLineMatch = descLine.match(/^description:\s*"(.*)"\s*$/);
|
|
102
|
-
if (!singleLineMatch) {
|
|
103
|
-
errors.push(
|
|
104
|
-
`${rel}: 'description' must be a double-quoted string on a single physical line ` +
|
|
105
|
-
`(blocks-cli's frontmatter parser reads it line-by-line -- a wrapped description silently truncates)`
|
|
106
|
-
);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const description = singleLineMatch[1];
|
|
111
|
-
if (description.length === 0) {
|
|
112
|
-
errors.push(`${rel}: 'description' is empty`);
|
|
113
|
-
} else if (description.length > DESCRIPTION_HARD_LIMIT) {
|
|
114
|
-
errors.push(`${rel}: description is ${description.length} chars, over the ${DESCRIPTION_HARD_LIMIT}-char hard limit`);
|
|
115
|
-
} else if (description.length > DESCRIPTION_WARN_LIMIT) {
|
|
116
|
-
warnings.push(`${rel}: description is ${description.length} chars, over the ${DESCRIPTION_WARN_LIMIT}-char house-style target (aim for ~400-600)`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function checkLinksInFile(skillName, filePath) {
|
|
121
|
-
const raw = readFileSync(filePath, "utf8");
|
|
122
|
-
const rel = relative(skillsDir, filePath);
|
|
123
|
-
const linkPattern = /\[[^\]]*\]\(([^)]+)\)/g;
|
|
124
|
-
const fileDir = dirname(filePath);
|
|
125
|
-
|
|
126
|
-
for (const match of raw.matchAll(linkPattern)) {
|
|
127
|
-
const target = match[1].trim();
|
|
128
|
-
if (/^[a-z]+:\/\//i.test(target) || target.startsWith("#")) continue; // external URL or in-page anchor
|
|
129
|
-
|
|
130
|
-
const [pathPart] = target.split("#");
|
|
131
|
-
if (!pathPart) continue;
|
|
132
|
-
|
|
133
|
-
const resolved = join(fileDir, pathPart);
|
|
134
|
-
if (!existsSync(resolved)) {
|
|
135
|
-
errors.push(`${rel}: broken link to '${pathPart}' (resolved: ${relative(skillsDir, resolved)})`);
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const resolvedRelToSkills = relative(skillsDir, resolved).split(/[\\/]/);
|
|
140
|
-
const targetSkill = resolvedRelToSkills[0];
|
|
141
|
-
if (targetSkill !== skillName) {
|
|
142
|
-
errors.push(
|
|
143
|
-
`${rel}: link leaves this skill's own directory ('${pathPart}') -- ` +
|
|
144
|
-
`mention other skills by name in plain text instead, never a link, since ` +
|
|
145
|
-
`'blocks skill add' only copies one skill directory at a time and the target isn't guaranteed to be present`
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function checkEndpointsInFile(filePath) {
|
|
152
|
-
const raw = readFileSync(filePath, "utf8");
|
|
153
|
-
const rel = relative(skillsDir, filePath);
|
|
154
|
-
|
|
155
|
-
for (const match of raw.matchAll(ENDPOINT_PATTERN)) {
|
|
156
|
-
errors.push(`${rel}: raw API endpoint path '${match[0]}' -- describe the CLI command/SDK method instead, never the wire path`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
for (const warning of warnings) console.warn(`warning: ${warning}`);
|
|
161
|
-
if (errors.length === 0) {
|
|
162
|
-
console.log(`ok: ${skillDirs.length} skills, no problems found${warnings.length ? ` (${warnings.length} warning(s) above)` : ""}`);
|
|
163
|
-
process.exit(0);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
console.error(`${errors.length} problem(s) found:`);
|
|
167
|
-
for (const error of errors) console.error(` - ${error}`);
|
|
168
|
-
process.exit(1);
|