@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
package/dist/index.js
CHANGED
|
@@ -86,17 +86,13 @@ import { localizationValidate } from "./commands/localization/validate.js";
|
|
|
86
86
|
import { login } from "./commands/login.js";
|
|
87
87
|
import { logout } from "./commands/logout.js";
|
|
88
88
|
import { newWeb } from "./commands/new/web.js";
|
|
89
|
-
|
|
89
|
+
import { createProject } from "./commands/projects/create.js";
|
|
90
90
|
import { getProject } from "./commands/projects/get.js";
|
|
91
91
|
import { listProjects } from "./commands/projects/list.js";
|
|
92
92
|
import { releaseBuildsGet } from "./commands/release/builds/get.js";
|
|
93
93
|
import { releaseBuildsList } from "./commands/release/builds/list.js";
|
|
94
94
|
import { releaseDeploy } from "./commands/release/deploy.js";
|
|
95
95
|
import { releaseStatus } from "./commands/release/status.js";
|
|
96
|
-
import { sdkClient } from "./commands/sdk/client.js";
|
|
97
|
-
import { skillAdd } from "./commands/skill/add.js";
|
|
98
|
-
import { skillList } from "./commands/skill/list.js";
|
|
99
|
-
import { skillShow } from "./commands/skill/show.js";
|
|
100
96
|
import { useProject } from "./commands/use.js";
|
|
101
97
|
import { authClientCredentialsDelete } from "./commands/auth/client-credentials/delete.js";
|
|
102
98
|
import { authClientCredentialsList } from "./commands/auth/client-credentials/list.js";
|
|
@@ -200,6 +196,7 @@ const commands = {
|
|
|
200
196
|
"init": () => init(),
|
|
201
197
|
"login": login,
|
|
202
198
|
"logout": logout,
|
|
199
|
+
"projects:create": createProject,
|
|
203
200
|
"projects:list": listProjects,
|
|
204
201
|
"use": useProject,
|
|
205
202
|
"deselect": deselectProject,
|
|
@@ -401,10 +398,6 @@ const commands = {
|
|
|
401
398
|
"iam:me": iamMe,
|
|
402
399
|
"projects:get": getProject,
|
|
403
400
|
"new:web": newWeb,
|
|
404
|
-
"skill:list": skillList,
|
|
405
|
-
"skill:show": skillShow,
|
|
406
|
-
"skill:add": skillAdd,
|
|
407
|
-
"sdk:client": sdkClient,
|
|
408
401
|
};
|
|
409
402
|
const MAX_COMMAND_WORDS = 4;
|
|
410
403
|
function resolveCommand(argv) {
|
|
@@ -535,6 +528,18 @@ Auth:
|
|
|
535
528
|
Revoke the current refresh token when possible and remove local session data.
|
|
536
529
|
|
|
537
530
|
Projects:
|
|
531
|
+
blocks projects create <name> [--allow-duplicate-name] [--yes] [--dry-run] [--json]
|
|
532
|
+
Create a new Blocks project via /os/v4/Project/Create using the account
|
|
533
|
+
token (no project needs to be selected yet). Creates exactly one
|
|
534
|
+
application, always in the 'dev' environment -- environment, domain,
|
|
535
|
+
cookie domain, and production flag are fixed and not configurable here;
|
|
536
|
+
the domain sent is a placeholder the platform discards and replaces with
|
|
537
|
+
the one it assigns. Confirms first because it accepts the Blocks terms on
|
|
538
|
+
your behalf. Refuses a name already used by another project unless
|
|
539
|
+
--allow-duplicate-name is passed. Verifies the result against
|
|
540
|
+
Project/Gets and prints the new tenantId, tenantGroupId, and assigned
|
|
541
|
+
domain. Does not select the project -- run 'blocks use <tenantId>' next.
|
|
542
|
+
|
|
538
543
|
blocks projects list [--json]
|
|
539
544
|
List accessible Blocks projects via /os/v4/Project/Gets. Uses the
|
|
540
545
|
impersonated project session when a project is selected, otherwise the
|
|
@@ -616,6 +621,9 @@ IAM:
|
|
|
616
621
|
blocks iam permissions update <id> [same flags as create, plus --is-archived]
|
|
617
622
|
[--dry-run] [--yes] [--json]
|
|
618
623
|
blocks iam permissions by-severity [--json]
|
|
624
|
+
--type is IAM's ResourceType: 0 None, 1 Endpoint, 2 FrontendAction, 3 DataProtection.
|
|
625
|
+
--severity is PermissionSeverity, ordered most-severe-first, not least: 0 None,
|
|
626
|
+
1 Critical, 2 High, 3 Medium, 4 Low.
|
|
619
627
|
|
|
620
628
|
Resources (/iam/v4/iam/resource*):
|
|
621
629
|
blocks iam resources groups [--json]
|
|
@@ -647,11 +655,15 @@ IAM:
|
|
|
647
655
|
[--body '<json>'|--file <path>] [--dry-run] [--yes] [--json]
|
|
648
656
|
|
|
649
657
|
MFA (/iam/v4/mfa*, project-scoped: requires a selected project, impersonated project token only):
|
|
658
|
+
Every mfa-type/auth-type/user-mfa-type value is IAM's UserMfaType enum: 0 None, 1 TOTP,
|
|
659
|
+
2 Email, 3 Sms, 4 WhatsApp. Only 1 and 2 have a working provider; 3 and 4 are declared
|
|
660
|
+
but unimplemented. Leaving --user-mfa-type empty means no method is allowed, so MFA is
|
|
661
|
+
never actually required no matter what else the policy says.
|
|
650
662
|
blocks mfa config get [--json]
|
|
651
663
|
Read the tenant's MFA policy.
|
|
652
664
|
blocks mfa config save [--enable] [--require-for-all-users] [--allow-user-opt-out]
|
|
653
665
|
[--allow-backup-codes] [--backup-codes-count <n>]
|
|
654
|
-
[--user-mfa-type
|
|
666
|
+
[--user-mfa-type 1,2] [--required-roles a,b] [--exempt-roles a,b]
|
|
655
667
|
[--body '<json>'|--file <path>] [--dry-run] [--yes] [--json]
|
|
656
668
|
Save the tenant's MFA policy.
|
|
657
669
|
blocks mfa totp setup [--json]
|
|
@@ -670,9 +682,12 @@ MFA (/iam/v4/mfa*, project-scoped: requires a selected project, impersonated pro
|
|
|
670
682
|
blocks mfa resend <mfaId> [--send-phone-number-as-email-domain <domain>] [--json]
|
|
671
683
|
blocks mfa verify <mfaId> <code> --auth-type <n> [--from-token-call] [--json]
|
|
672
684
|
blocks mfa method set --mfa-type <n> [--json]
|
|
673
|
-
Switch the impersonated user's active MFA method.
|
|
685
|
+
Switch the impersonated user's active MFA method. IAM only branches on 1 (TOTP) and
|
|
686
|
+
2 (Email) here -- any other value falls through to its disable path and turns the
|
|
687
|
+
user's MFA off. Use 'blocks mfa disable' when that is what you mean.
|
|
674
688
|
blocks mfa disable [--dry-run] [--yes] [--json]
|
|
675
689
|
blocks mfa backup-codes list [--json]
|
|
690
|
+
Returns { remaining: <count> } only -- the codes themselves are shown once, at generate.
|
|
676
691
|
blocks mfa backup-codes generate [--dry-run] [--yes] [--json]
|
|
677
692
|
blocks mfa backup-codes use <userId> <code> [--json]
|
|
678
693
|
|
|
@@ -815,11 +830,11 @@ Auth Admin (/iam/v4/auth/identity-providers*, /config, /client-credentials, /oid
|
|
|
815
830
|
[--redirect-uris a,b] [--post-logout-redirect-uris a,b]
|
|
816
831
|
[--scope] [--allowed-scopes a,b] [--allowed-response-types a,b]
|
|
817
832
|
[--require-pkce] [--require-consent] [--require-mfa]
|
|
818
|
-
[--allowed-mfa-methods
|
|
833
|
+
[--allowed-mfa-methods 1,2] [--front-channel-logout-uri]
|
|
819
834
|
[--back-channel-logout-uri] [--auto-redirect]
|
|
820
835
|
[--external-discovery-endpoint] [--active] [--login-mode]
|
|
821
836
|
[--client-logo-url] [--client-brand-color] [--use-tokens-cookie]
|
|
822
|
-
[--register-as-identity-provider] [--device-flow-client]
|
|
837
|
+
[--register-as-identity-provider] [--oidc-url] [--device-flow-client]
|
|
823
838
|
[--body '<json>'|--file <path>] [--dry-run] [--yes] [--json]
|
|
824
839
|
Upsert: omit --item-id to register a new client, pass it to update an existing one.
|
|
825
840
|
The response's client_secret is shown once and is not retrievable again afterward.
|
|
@@ -828,9 +843,10 @@ Auth Admin (/iam/v4/auth/identity-providers*, /config, /client-credentials, /oid
|
|
|
828
843
|
lets it request the client_credentials grant. Pass --client-type public for any
|
|
829
844
|
PKCE/browser client.
|
|
830
845
|
--register-as-identity-provider creates the linked identity provider in the same call.
|
|
831
|
-
Its authorize/token/userinfo/jwks/issuer values are filled from the discovery document
|
|
832
|
-
|
|
833
|
-
|
|
846
|
+
Its authorize/token/userinfo/jwks/issuer values are filled from the discovery document.
|
|
847
|
+
When omitted, --external-discovery-endpoint defaults to
|
|
848
|
+
<oidc-url>/<project>/.well-known/openid-configuration; pass it explicitly for an
|
|
849
|
+
external provider or non-standard IAM base URL.
|
|
834
850
|
blocks auth oidc-clients delete <clientId> [--dry-run] [--yes] [--json]
|
|
835
851
|
Irreversible; revokes all tokens issued to the client.
|
|
836
852
|
blocks auth oidc-clients rotate-secret <clientId> [--dry-run] [--yes] [--json]
|
|
@@ -906,6 +922,7 @@ Data:
|
|
|
906
922
|
Irreversible.
|
|
907
923
|
blocks data schema info list [--json]
|
|
908
924
|
Entity-type schema collections with basic info.
|
|
925
|
+
--schema-type: 1 Entity, 2 Dto. There is no 0.
|
|
909
926
|
blocks data schema info save --schema-name <n> [--collection-name] [--schema-type <1|2>]
|
|
910
927
|
[--body '<json>'|--file <path>] [--dry-run] [--yes] [--json]
|
|
911
928
|
Create schema metadata only (no fields yet) - pair with data schema fields.
|
|
@@ -1090,6 +1107,7 @@ Localization:
|
|
|
1090
1107
|
blocks localization key uilm-import <fileId> [--message-co-relation-id]
|
|
1091
1108
|
[--dry-run] [--yes] [--json]
|
|
1092
1109
|
blocks localization key uilm-export [--output-type <0-5>] [--app-ids a,b] [--languages a,b]
|
|
1110
|
+
--output-type: 0 Json (default), 1 Xml, 2 Text, 3 Xlsx, 4 Csv, 5 Xlf.
|
|
1093
1111
|
[--reference-file-id] [--caller-tenant-id] [--start-date]
|
|
1094
1112
|
[--end-date] [--message-co-relation-id] [--dry-run] [--yes] [--json]
|
|
1095
1113
|
blocks localization key get-uilm-exported-files [--search] [--page-number] [--page-size]
|
|
@@ -1163,29 +1181,5 @@ Scaffold:
|
|
|
1163
1181
|
different Data/IAM/Localization/OS gateway URL explicitly only if your
|
|
1164
1182
|
project uses a non-default one.
|
|
1165
1183
|
--oidc-url defaults to https://iam.seliseblocks.com.
|
|
1166
|
-
|
|
1167
|
-
Skills:
|
|
1168
|
-
blocks skill list [--json]
|
|
1169
|
-
List bundled blocks-skills/*/SKILL.md agent context docs (name +
|
|
1170
|
-
description). Local-only, no cloud calls.
|
|
1171
|
-
blocks skill show <name> [--json]
|
|
1172
|
-
Print one skill's full SKILL.md content.
|
|
1173
|
-
blocks skill add <name> [--dir <path>]
|
|
1174
|
-
Copy a bundled skill's SKILL.md into <path>/<name>/SKILL.md in the
|
|
1175
|
-
current directory (default --dir is 'blocks-skills'), for use in a
|
|
1176
|
-
project outside this monorepo. Overwrites silently, same as
|
|
1177
|
-
'data schema pull'.
|
|
1178
|
-
|
|
1179
|
-
SDK:
|
|
1180
|
-
blocks sdk client [--app-domain <domain>] [--client-id <oidcClientId>]
|
|
1181
|
-
[--x-blocks-key <tenantId>] [--blocks-api-url <url>] [--oidc-url <url>] [--json]
|
|
1182
|
-
Read-only: "I want to use the Blocks SDK -- show me the client." Resolves this
|
|
1183
|
-
project's @seliseblocks/client config using the selected project unless
|
|
1184
|
-
--x-blocks-key overrides it, and the project's registered domain/OIDC client
|
|
1185
|
-
when --app-domain/--client-id are omitted. Its API URL defaults to
|
|
1186
|
-
https://api.seliseblocks.com unless --blocks-api-url is passed.
|
|
1187
|
-
Prints a ready-to-paste createBlocksClient(...) snippet.
|
|
1188
|
-
Passing both --app-domain and --client-id skips the project lookup entirely
|
|
1189
|
-
(no login required). Never writes a file; to scaffold a new app use 'new web'.
|
|
1190
1184
|
`);
|
|
1191
1185
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function blocksOidcWellKnownUrl(oidcUrl, tenantId) {
|
|
2
|
+
return `${oidcUrl.replace(/\/$/, "")}/${tenantId}/.well-known/openid-configuration`;
|
|
3
|
+
}
|
|
4
|
+
export function withBlocksIdentityProviderDiscovery(body, oidcUrl, tenantId) {
|
|
5
|
+
if (body.registerAsIdentityProvider !== true || (typeof body.externalDiscoveryEndpoint === "string" && body.externalDiscoveryEndpoint)) {
|
|
6
|
+
return body;
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
...body,
|
|
10
|
+
externalDiscoveryEndpoint: blocksOidcWellKnownUrl(oidcUrl, tenantId)
|
|
11
|
+
};
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@seliseblocks/cli-os",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "CLI for SELISE Blocks project setup and configuration.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"blocks": "bin/run.js"
|
|
9
|
-
},
|
|
10
|
-
"main": "./dist/index.js",
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"exports": {
|
|
13
|
-
".": {
|
|
14
|
-
"types": "./dist/index.d.ts",
|
|
15
|
-
"import": "./dist/index.js"
|
|
16
|
-
},
|
|
17
|
-
"./package.json": "./package.json"
|
|
18
|
-
},
|
|
19
|
-
"publishConfig": {
|
|
20
|
-
"access": "public"
|
|
21
|
-
},
|
|
22
|
-
"sideEffects": false,
|
|
23
|
-
"files": [
|
|
24
|
-
"bin",
|
|
25
|
-
"dist",
|
|
26
|
-
"README.md",
|
|
27
|
-
"AI_USAGE_GUIDE.md",
|
|
28
|
-
"LICENSE"
|
|
29
|
-
],
|
|
30
|
-
"scripts": {
|
|
31
|
-
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
32
|
-
"build": "npm run clean && tsc -p tsconfig.json
|
|
33
|
-
"dev": "tsx src/index.ts",
|
|
34
|
-
"lint": "tsc -p tsconfig.json --noEmit",
|
|
35
|
-
"test": "npm run build && node --test test/*.test.mjs",
|
|
36
|
-
"prepack": "npm run build",
|
|
37
|
-
"prepublishOnly": "npm test"
|
|
38
|
-
},
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"@types/node": "^22.0.0",
|
|
41
|
-
"tsx": "^4.16.0",
|
|
42
|
-
"typescript": "^5.5.0"
|
|
43
|
-
},
|
|
44
|
-
"engines": {
|
|
45
|
-
"node": ">=20"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@seliseblocks/cli-os",
|
|
3
|
+
"version": "0.2.12",
|
|
4
|
+
"description": "CLI for SELISE Blocks project setup and configuration.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"blocks": "bin/run.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"files": [
|
|
24
|
+
"bin",
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md",
|
|
27
|
+
"AI_USAGE_GUIDE.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
32
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
33
|
+
"dev": "tsx src/index.ts",
|
|
34
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
35
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
36
|
+
"prepack": "npm run build",
|
|
37
|
+
"prepublishOnly": "npm test"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.0.0",
|
|
41
|
+
"tsx": "^4.16.0",
|
|
42
|
+
"typescript": "^5.5.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function sdkClient(argv: string[]): Promise<void>;
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { blocksRequest } from "../../lib/api.js";
|
|
2
|
-
import { stringFlag } from "../../lib/args.js";
|
|
3
|
-
import { defaults } from "../../lib/config.js";
|
|
4
|
-
import { findProjectByTenantId } from "../../lib/project-info.js";
|
|
5
|
-
import { writeOutput } from "../../lib/output.js";
|
|
6
|
-
import { requestContext } from "../../lib/request-context.js";
|
|
7
|
-
import { parseCommand, selectedProject } from "../../lib/workspace.js";
|
|
8
|
-
// Read-only: "I want to use the Blocks SDK in my app -- show me the client."
|
|
9
|
-
// Resolves this project's createBlocksClient() config and prints it, same
|
|
10
|
-
// values 'new web' scaffolds with. Never writes a file and never mutates
|
|
11
|
-
// anything -- if you want the SDK wired into a new app, use 'new web'.
|
|
12
|
-
export async function sdkClient(argv) {
|
|
13
|
-
const { flags } = parseCommand(argv);
|
|
14
|
-
const tenantId = stringFlag(flags, "x-blocks-key") || (await selectedProject(flags));
|
|
15
|
-
const apiUrl = stringFlag(flags, "blocks-api-url", { defaultValue: defaults().apiUrl });
|
|
16
|
-
const oidcUrl = stringFlag(flags, "oidc-url", { defaultValue: defaults().oidcUrl });
|
|
17
|
-
let appDomain = stringFlag(flags, "app-domain");
|
|
18
|
-
let oidcClientId = stringFlag(flags, "client-id");
|
|
19
|
-
const notes = [];
|
|
20
|
-
if (!appDomain || !oidcClientId) {
|
|
21
|
-
const { project } = await findProjectByTenantId(tenantId, flags);
|
|
22
|
-
if (!appDomain) {
|
|
23
|
-
const domains = (project.applications ?? [])
|
|
24
|
-
.map((application) => application.domain)
|
|
25
|
-
.filter((domain) => Boolean(domain));
|
|
26
|
-
if (domains.length === 1) {
|
|
27
|
-
appDomain = domains[0];
|
|
28
|
-
}
|
|
29
|
-
else if (domains.length > 1) {
|
|
30
|
-
notes.push(`Multiple domains registered for this project (${domains.join(", ")}) -- pass --app-domain to pick one.`);
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
notes.push("This project has no domains registered in Blocks -- pass --app-domain explicitly.");
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (!oidcClientId) {
|
|
37
|
-
const clients = await listOidcClients(tenantId, flags);
|
|
38
|
-
if (clients.length === 1) {
|
|
39
|
-
oidcClientId = clients[0].id;
|
|
40
|
-
}
|
|
41
|
-
else if (clients.length > 1) {
|
|
42
|
-
notes.push(`Multiple OIDC clients found (${clients.map((client) => `${client.label} [${client.id}]`).join(", ")}) -- pass --client-id to pick one.`);
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
notes.push("No OIDC client registered for this project -- create one ('auth:oidc-clients:save') and pass --client-id.");
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (flags.json) {
|
|
50
|
-
writeOutput({ apiUrl, appDomain: appDomain || undefined, notes, oidcClientId: oidcClientId || undefined, oidcUrl, xBlocksKey: tenantId }, flags);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
console.log("import { createBlocksClient } from \"@seliseblocks/client\";");
|
|
54
|
-
console.log("");
|
|
55
|
-
console.log("export const blocksClient = createBlocksClient({");
|
|
56
|
-
console.log(` apiUrl: "${apiUrl}",`);
|
|
57
|
-
if (appDomain)
|
|
58
|
-
console.log(` appDomain: "${appDomain}",`);
|
|
59
|
-
console.log(" oidc: {");
|
|
60
|
-
console.log(` clientId: "${oidcClientId || "<register a public OIDC client, see auth:oidc-clients:save>"}",`);
|
|
61
|
-
console.log(" scope: \"openid profile\",");
|
|
62
|
-
console.log(` url: "${oidcUrl}"`);
|
|
63
|
-
console.log(" },");
|
|
64
|
-
console.log(` xBlocksKey: "${tenantId}"`);
|
|
65
|
-
console.log("});");
|
|
66
|
-
for (const note of notes)
|
|
67
|
-
console.log(`\n${note}`);
|
|
68
|
-
}
|
|
69
|
-
async function listOidcClients(tenantId, flags) {
|
|
70
|
-
const raw = await blocksRequest("/iam/v4/oidc-clients", {
|
|
71
|
-
impersonatedProjectAuth: true,
|
|
72
|
-
projectTenantId: tenantId,
|
|
73
|
-
...requestContext(flags)
|
|
74
|
-
});
|
|
75
|
-
const clients = [];
|
|
76
|
-
for (const item of normalizeList(raw)) {
|
|
77
|
-
if (typeof item !== "object" || item === null)
|
|
78
|
-
continue;
|
|
79
|
-
const record = item;
|
|
80
|
-
const id = record.itemId ?? record.clientId ?? record.id;
|
|
81
|
-
if (typeof id !== "string" || !id)
|
|
82
|
-
continue;
|
|
83
|
-
const label = typeof record.clientDisplayName === "string" && record.clientDisplayName ? record.clientDisplayName : id;
|
|
84
|
-
clients.push({ id, label });
|
|
85
|
-
}
|
|
86
|
-
return clients;
|
|
87
|
-
}
|
|
88
|
-
function normalizeList(raw) {
|
|
89
|
-
if (Array.isArray(raw))
|
|
90
|
-
return raw;
|
|
91
|
-
if (raw && typeof raw === "object") {
|
|
92
|
-
for (const key of ["data", "items", "results"]) {
|
|
93
|
-
const value = raw[key];
|
|
94
|
-
if (Array.isArray(value))
|
|
95
|
-
return value;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return [];
|
|
99
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function skillAdd(argv: string[]): Promise<void>;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { cp } from "node:fs/promises";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { stringFlag } from "../../lib/args.js";
|
|
4
|
-
import { readSkill } from "../../lib/skills.js";
|
|
5
|
-
import { parseCommand } from "../../lib/workspace.js";
|
|
6
|
-
export async function skillAdd(argv) {
|
|
7
|
-
const { args, flags } = parseCommand(argv);
|
|
8
|
-
const name = args[0];
|
|
9
|
-
if (!name)
|
|
10
|
-
throw new Error("Missing skill name. Run 'blocks skill list' to see available skills.");
|
|
11
|
-
const skill = await readSkill(name);
|
|
12
|
-
const sourceDir = dirname(skill.path);
|
|
13
|
-
const targetDir = stringFlag(flags, "dir", { defaultValue: "blocks-skills" });
|
|
14
|
-
const targetPath = join(process.cwd(), targetDir, name);
|
|
15
|
-
// Copy the whole skill directory, not just SKILL.md -- some skills also ship
|
|
16
|
-
// supporting files (e.g. flows/*.md) that SKILL.md links to.
|
|
17
|
-
await cp(sourceDir, targetPath, { recursive: true });
|
|
18
|
-
console.log(`Added ${targetPath}`);
|
|
19
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function skillList(argv: string[]): Promise<void>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { listSkills, SKILLS_REPO_URL } from "../../lib/skills.js";
|
|
2
|
-
import { writeOutput } from "../../lib/output.js";
|
|
3
|
-
import { parseCommand } from "../../lib/workspace.js";
|
|
4
|
-
export async function skillList(argv) {
|
|
5
|
-
const { flags } = parseCommand(argv);
|
|
6
|
-
const skills = await listSkills();
|
|
7
|
-
if (flags.json) {
|
|
8
|
-
writeOutput(skills, flags);
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
for (const skill of skills) {
|
|
12
|
-
console.log(`${skill.name} ${skill.description}`);
|
|
13
|
-
}
|
|
14
|
-
console.log(`\nFull catalog (may be ahead of this bundled list): ${SKILLS_REPO_URL}`);
|
|
15
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function skillShow(argv: string[]): Promise<void>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { readSkill } from "../../lib/skills.js";
|
|
2
|
-
import { writeOutput } from "../../lib/output.js";
|
|
3
|
-
import { parseCommand } from "../../lib/workspace.js";
|
|
4
|
-
export async function skillShow(argv) {
|
|
5
|
-
const { args, flags } = parseCommand(argv);
|
|
6
|
-
const name = args[0];
|
|
7
|
-
if (!name)
|
|
8
|
-
throw new Error("Missing skill name. Run 'blocks skill list' to see available skills.");
|
|
9
|
-
const skill = await readSkill(name);
|
|
10
|
-
if (flags.json) {
|
|
11
|
-
writeOutput(skill, flags);
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
console.log(skill.content);
|
|
15
|
-
}
|
package/dist/lib/skills.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export type SkillSummary = {
|
|
2
|
-
description: string;
|
|
3
|
-
name: string;
|
|
4
|
-
path: string;
|
|
5
|
-
};
|
|
6
|
-
export type SkillDetail = SkillSummary & {
|
|
7
|
-
content: string;
|
|
8
|
-
};
|
|
9
|
-
export declare const SKILLS_REPO_URL = "https://github.com/SELISEdigitalplatforms/blocks-cli/tree/main/blocks-skills";
|
|
10
|
-
export declare function resolveSkillsDir(): string;
|
|
11
|
-
export declare function parseFrontmatter(raw: string): {
|
|
12
|
-
body: string;
|
|
13
|
-
description?: string;
|
|
14
|
-
name?: string;
|
|
15
|
-
};
|
|
16
|
-
export declare function listSkills(): Promise<SkillSummary[]>;
|
|
17
|
-
export declare function readSkill(name: string): Promise<SkillDetail>;
|
package/dist/lib/skills.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
// Canonical public catalog -- the source of truth when the locally bundled
|
|
6
|
-
// skills are missing a name, or out of date relative to what's published.
|
|
7
|
-
export const SKILLS_REPO_URL = "https://github.com/SELISEdigitalplatforms/blocks-cli/tree/main/blocks-skills";
|
|
8
|
-
// Resolves where blocks-skills/*/SKILL.md content lives, in priority order:
|
|
9
|
-
// 1. Bundled into this package at build time (see scripts/copy-skills.mjs) --
|
|
10
|
-
// what a published npm install actually ships.
|
|
11
|
-
// 2. The monorepo root's blocks-skills/ folder -- covers 'npm run dev' (tsx,
|
|
12
|
-
// no build step) and running straight from a source checkout.
|
|
13
|
-
export function resolveSkillsDir() {
|
|
14
|
-
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
const candidates = [join(moduleDir, "..", "skills"), join(moduleDir, "..", "..", "..", "blocks-skills")];
|
|
16
|
-
for (const candidate of candidates) {
|
|
17
|
-
if (existsSync(candidate))
|
|
18
|
-
return candidate;
|
|
19
|
-
}
|
|
20
|
-
throw new Error("No blocks-skills content found. Expected a bundled 'skills' folder next to this package, or a 'blocks-skills' folder at the monorepo root.");
|
|
21
|
-
}
|
|
22
|
-
// Hand-rolled parser for this repo's flat, single-line SKILL.md frontmatter
|
|
23
|
-
// (`name: ...` / `description: "..."`) -- no YAML dependency exists in this
|
|
24
|
-
// package and none of these fields span multiple lines.
|
|
25
|
-
export function parseFrontmatter(raw) {
|
|
26
|
-
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
27
|
-
if (!match)
|
|
28
|
-
return { body: raw };
|
|
29
|
-
const [, frontmatter, body] = match;
|
|
30
|
-
const fields = {};
|
|
31
|
-
for (const line of frontmatter.split(/\r?\n/)) {
|
|
32
|
-
const separator = line.indexOf(":");
|
|
33
|
-
if (separator === -1)
|
|
34
|
-
continue;
|
|
35
|
-
const key = line.slice(0, separator).trim();
|
|
36
|
-
let value = line.slice(separator + 1).trim();
|
|
37
|
-
if (value.length >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
|
|
38
|
-
value = value.slice(1, -1);
|
|
39
|
-
}
|
|
40
|
-
fields[key] = value;
|
|
41
|
-
}
|
|
42
|
-
return { body, description: fields.description, name: fields.name };
|
|
43
|
-
}
|
|
44
|
-
export async function listSkills() {
|
|
45
|
-
const dir = resolveSkillsDir();
|
|
46
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
47
|
-
const summaries = [];
|
|
48
|
-
for (const entry of entries) {
|
|
49
|
-
if (!entry.isDirectory())
|
|
50
|
-
continue;
|
|
51
|
-
const skillPath = join(dir, entry.name, "SKILL.md");
|
|
52
|
-
if (!existsSync(skillPath))
|
|
53
|
-
continue;
|
|
54
|
-
const raw = await readFile(skillPath, "utf8");
|
|
55
|
-
const { description, name } = parseFrontmatter(raw);
|
|
56
|
-
summaries.push({ description: description ?? "", name: name ?? entry.name, path: skillPath });
|
|
57
|
-
}
|
|
58
|
-
return summaries.sort((a, b) => a.name.localeCompare(b.name));
|
|
59
|
-
}
|
|
60
|
-
export async function readSkill(name) {
|
|
61
|
-
const skills = await listSkills();
|
|
62
|
-
const match = skills.find((skill) => skill.name === name);
|
|
63
|
-
if (!match) {
|
|
64
|
-
const available = skills.map((skill) => skill.name).join(", ") || "(none found)";
|
|
65
|
-
throw new Error(`Unknown skill '${name}'. Available skills: ${available}. If the skill you're looking for isn't listed (the bundled set may be out of date), check the full catalog at ${SKILLS_REPO_URL}.`);
|
|
66
|
-
}
|
|
67
|
-
const content = await readFile(match.path, "utf8");
|
|
68
|
-
return { ...match, content };
|
|
69
|
-
}
|