@seliseblocks/cli-os 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/AI_USAGE_GUIDE.md +20 -15
  2. package/README.md +5 -5
  3. package/dist/commands/auth/idp/create.d.ts +7 -0
  4. package/dist/commands/auth/idp/create.js +22 -3
  5. package/dist/commands/auth/idp/update.d.ts +7 -0
  6. package/dist/commands/auth/idp/update.js +22 -3
  7. package/dist/commands/data/files/delete.js +5 -4
  8. package/dist/commands/data/files/get-many.js +1 -1
  9. package/dist/commands/data/files/get.js +1 -1
  10. package/dist/commands/data/files/info.js +1 -1
  11. package/dist/commands/data/files/object-tree.d.ts +23 -0
  12. package/dist/commands/data/files/object-tree.js +238 -0
  13. package/dist/commands/data/files/presigned-upload-url.js +9 -2
  14. package/dist/commands/data/files/update-additional-info.js +2 -2
  15. package/dist/commands/data/files/upload-to-local-storage.js +2 -2
  16. package/dist/commands/data/files/upload.d.ts +2 -4
  17. package/dist/commands/data/files/upload.js +14 -28
  18. package/dist/commands/new/web.js +4 -0
  19. package/dist/index.js +100 -42
  20. package/dist/skills/blocks-data-storage/SKILL.md +194 -102
  21. package/dist/skills/blocks-data-storage/flows/object-management.md +124 -0
  22. package/dist/skills/blocks-iam-sso-oidc-configuration/SKILL.md +25 -9
  23. package/dist/skills/blocks-onboarding/SKILL.md +2 -2
  24. package/dist/skills/blocks-storage-configuration/SKILL.md +93 -93
  25. package/package.json +1 -1
  26. package/dist/commands/data/files/create-folder.d.ts +0 -1
  27. package/dist/commands/data/files/create-folder.js +0 -34
  28. package/dist/commands/data/files/delete-folder.d.ts +0 -1
  29. package/dist/commands/data/files/delete-folder.js +0 -25
  30. package/dist/commands/data/files/dms-list.d.ts +0 -1
  31. package/dist/commands/data/files/dms-list.js +0 -27
  32. package/dist/commands/data/files/dms-upload.d.ts +0 -6
  33. package/dist/commands/data/files/dms-upload.js +0 -41
package/AI_USAGE_GUIDE.md CHANGED
@@ -128,10 +128,10 @@ Then run with explicit flags so no prompt is reached:
128
128
  blocks new web <appName> --x-blocks-key <projectTenantId> --app-domain <appDomainOrUrl> --client-id <publicOidcClientId>
129
129
  ```
130
130
 
131
- `new web` also accepts `--blocks-api-url <url>` and `--oidc-url <url>`, same as `sdk client`
132
- below. When `--blocks-api-url` is omitted, the scaffold derives it from the app domain as
133
- `https://blocksapi.<registrable-domain>`; for example `https://dqrsf.slsblx.com` becomes
134
- `https://blocksapi.slsblx.com`. Pass `--blocks-api-url` only when targeting a non-default Blocks gateway. `--oidc-url` defaults to `https://iam.seliseblocks.com`.
131
+ `new web` also accepts `--blocks-api-url <url>` and `--oidc-url <url>`, same as `sdk client`
132
+ below. When `--blocks-api-url` is omitted, the scaffold derives it from the app domain as
133
+ `https://blocksapi.<registrable-domain>`; for example `https://dqrsf.slsblx.com` becomes
134
+ `https://blocksapi.slsblx.com`. Pass `--blocks-api-url` only when targeting a non-default Blocks gateway. `--oidc-url` defaults to `https://iam.seliseblocks.com`.
135
135
 
136
136
  Validate the scaffold:
137
137
 
@@ -160,10 +160,10 @@ The generated cert script uses the `selfsigned` Node dependency, so it works fro
160
160
  `sdk client` answers "I want to use the Blocks SDK - show me the client." It resolves this project's `@seliseblocks/client` config (same values `new web` scaffolds an app with) and prints a ready-to-paste `createBlocksClient(...)` snippet - **it never writes a file or mutates anything**. To scaffold a full app instead, use `new web` above.
161
161
 
162
162
  ```bash
163
- blocks sdk client --x-blocks-key <projectTenantId> --app-domain <appDomainOrUrl> --client-id <publicOidcClientId> --blocks-api-url https://api.seliseblocks.com
163
+ blocks sdk client --x-blocks-key <projectTenantId> --app-domain <appDomainOrUrl> --client-id <publicOidcClientId> --blocks-api-url https://api.seliseblocks.com
164
164
  ```
165
165
 
166
- Unlike `new web`, `sdk client` keeps `--blocks-api-url` defaulted to `https://api.seliseblocks.com`; only pass it explicitly if your project uses a different gateway URL. Passing both `--app-domain` and `--client-id` skips the project lookup entirely, so it needs no CLI login at all - useful for a quick, non-interactive check. Omit either one and it resolves from the selected project instead (auto-picks when there's exactly one match, otherwise lists the options and asks you to pass the flag explicitly - it does not prompt or create anything, since this command is read-only). Use `--json` for the resolved values instead of the snippet.
166
+ Unlike `new web`, `sdk client` keeps `--blocks-api-url` defaulted to `https://api.seliseblocks.com`; only pass it explicitly if your project uses a different gateway URL. Passing both `--app-domain` and `--client-id` skips the project lookup entirely, so it needs no CLI login at all - useful for a quick, non-interactive check. Omit either one and it resolves from the selected project instead (auto-picks when there's exactly one match, otherwise lists the options and asks you to pass the flag explicitly - it does not prompt or create anything, since this command is read-only). Use `--json` for the resolved values instead of the snippet.
167
167
 
168
168
  ## Skills
169
169
 
@@ -181,9 +181,9 @@ Every other command below is project-scoped: it requires a project already selec
181
181
 
182
182
  Command families (run `blocks --help` for the full flag reference on each):
183
183
 
184
- - `iam users *`, `iam email available` - list/get/create/update/activate/deactivate, access grant/revoke, existence and email-availability checks.
185
- - `iam roles *` - list/get/create/update, assign-permissions, assignable. `assign-permissions` accepts permission resource strings and resolves them to itemIds before sending IAM's id-based mutation.
186
- - `iam permissions *` - list/get/create/update, by-severity.
184
+ - `iam users *`, `iam email available` - list/get/create/update/activate/deactivate, access grant/revoke, existence and email-availability checks.
185
+ - `iam roles *` - list/get/create/update, assign-permissions, assignable. `assign-permissions` accepts permission resource strings and resolves them to itemIds before sending IAM's id-based mutation.
186
+ - `iam permissions *` - list/get/create/update, by-severity.
187
187
  - `iam resources *` - resource groups and feature flags (read-only).
188
188
  - `iam organizations *` - list/get/create/update, `my`, and organization config get/save.
189
189
  - `iam signup-settings *` - get/save tenant signup policy.
@@ -280,13 +280,13 @@ It validates first and hard-fails with no API calls made if schemas or the rules
280
280
  - `data schema info list`/`save`/`update` + `data schema fields` - a two-step alternative to `schema push` (create/update schema metadata, then add/update field definitions separately). Prefer the file-oriented `schema push` workflow for normal authoring; use these only for a targeted metadata or field-only change without touching the local schema JSON.
281
281
  - `data rules policy get`/`delete` - read or delete one data-access policy without a full `rules pull`/edit/`rules deploy` round-trip.
282
282
  - `data validation list`/`get`/`by-schema`/`by-schema-field`/`save`/`delete` - field-level validation rules. No file-oriented workflow exists for these (no local JSON file to pull/push). `save` is an upsert (omit `--item-id` to create, pass it to update) and requires a `validations` array passed via `--body`/`--file` - there's no scalar flag for it, e.g. `--body '{"validations":[{"type":1,"value":"^[0-9]+$","isActive":true}]}'`.
283
- - `data files *` - DMS/storage: `get`/`get-many`/`info` (read), `presigned-upload-url` + `upload-to-url` (cloud storage, two calls) or `upload-to-local-storage` (one call, local storage), `update-additional-info`, `delete`, `dms-list`/`dms-upload` (folder browsing / registering an uploaded file into a folder), `create-folder`/`delete-folder`.
283
+ - `data files *` - permission-aware storage object tree: upload/download, directory CRUD/move, cursor list/search, versions, copy/move/rename, trash/restore/purge, shared objects, and access policies/inheritance.
284
284
 
285
285
  Same rules as everywhere else: `--dry-run` before any mutating command, then `--yes` only after explicit approval.
286
286
 
287
287
  **`--file` means two different things depending on the command.** Everywhere else in this CLI (`--body '<json>'`/`--file <path.json>`), `--file` is a JSON payload file read by `jsonBodyFlag`. On the `data files *` upload commands (`upload-to-url`, `upload-to-local-storage`), `--file` is instead the local binary file to read and upload - there is no JSON payload involved. Don't conflate the two: passing a JSON path to `data files upload-to-local-storage --file` uploads the JSON text as the file's bytes, it does not set a request body.
288
288
 
289
- **Prefer the composed `data files upload` over the manual steps below.** It runs presign PUT dms-upload for you (or the one-call local-storage path with `--local-storage`), so the file is both stored and visible in DMS afterward - no copy-pasting `uploadUrl`/`fileId` between commands:
289
+ **Prefer the composed `data files upload` over the manual steps below.** For cloud storage it creates the file/version metadata and PUTs the bytes; for local storage it performs one multipart call. Either path creates the visible object directly—there is no DMS registration step:
290
290
 
291
291
  ```bash
292
292
  blocks data files upload --file ./invoice.pdf --access-modifier Public --dry-run --json
@@ -297,7 +297,8 @@ blocks data files upload --file ./invoice.pdf --local-storage --yes --json # l
297
297
  Manual cloud-storage upload, if you need the intermediate steps for some reason (two calls):
298
298
 
299
299
  ```bash
300
- blocks data files presigned-upload-url --name invoice.pdf --access-modifier Public --json
300
+ blocks data files presigned-upload-url --name invoice.pdf --access-modifier Public --dry-run --json
301
+ blocks data files presigned-upload-url --name invoice.pdf --access-modifier Public --yes --json
301
302
  # take the returned uploadUrl and fileId, then:
302
303
  blocks data files upload-to-url --url "<uploadUrl>" --file ./invoice.pdf --content-type application/pdf --dry-run --json
303
304
  blocks data files upload-to-url --url "<uploadUrl>" --file ./invoice.pdf --content-type application/pdf --yes --json
@@ -310,11 +311,15 @@ blocks data files upload-to-local-storage --file ./invoice.pdf --access-modifier
310
311
  blocks data files upload-to-local-storage --file ./invoice.pdf --access-modifier Public --yes --json
311
312
  ```
312
313
 
313
- Either manual upload path only stores the bytes - it does not make the file appear in a DMS folder. Register it afterward if the user needs that (the composed `data files upload` above already does this step for you):
314
+ Browse the resulting object tree with cursor pagination. Deletion defaults to trash:
314
315
 
315
316
  ```bash
316
- blocks data files dms-upload --file-storage-id <fileId> --artifact-name invoice.pdf --dry-run --json
317
- blocks data files dms-upload --file-storage-id <fileId> --artifact-name invoice.pdf --yes --json
317
+ blocks data files list --parent-id <directoryId> --limit 50 --json
318
+ blocks data files search invoice --directory-id <directoryId> --json
319
+ blocks data files delete <fileId> --dry-run --json
320
+ blocks data files delete <fileId> --yes --json
321
+ blocks data files trash --json
322
+ blocks data files restore <fileId> --dry-run --json
318
323
  ```
319
324
 
320
325
  ## Localization
package/README.md CHANGED
@@ -84,8 +84,8 @@ Global options available on every command:
84
84
  | `blocks data schema get/get-by-name/aggregation/change-logs/delete/fields/info *` | Raw `/data/v4/schemas*` API beyond the file-oriented `list/pull/push` above: single-schema/by-name lookups, access-level aggregation, unadapted change logs, delete, and a metadata-first (`info *`) plus field-only (`fields`) alternative to `push`. Run `blocks --help` for the full flag reference. |
85
85
  | `blocks data rules policy get/delete` | Single data-access policy read/delete via `/data/v4/data-access/policy/*`, alongside the bulk file-oriented `rules pull/deploy` above. |
86
86
  | `blocks data validation list/get/by-schema/by-schema-field/save/delete` | Field-level validation rules via `/data/v4/data-validations*`. No file-oriented workflow exists for this yet; `save` is an upsert (omit `--item-id` to create) and requires a `validations` array via `--body`/`--file`. |
87
- | `blocks data files upload --file <path> [--local-storage]` | Composed upload: presigned-upload-url PUT the file dms-upload, threading the returned `uploadUrl`/`fileId` automatically (content-type inferred from the file extension if omitted). Pass `--local-storage` for the one-call `upload-to-local-storage` path instead. Prefer this over the individual steps below. |
88
- | `blocks data files *` | Full DMS/storage surface via `/data/v4/Files/*`: `get`/`get-many`/`info` (read), `presigned-upload-url` + `upload-to-url` (cloud storage, two steps) or `upload-to-local-storage` (one step, local storage), `update-additional-info`, `delete`, `dms-list`/`dms-upload` (folder browsing/file registration), `create-folder`/`delete-folder`. Here `--file <path>` is the local binary to upload/read, not a JSON payload file - see [AI_USAGE_GUIDE.md](AI_USAGE_GUIDE.md) for the distinction. |
87
+ | `blocks data files upload --file <path> [--local-storage]` | Composed upload: cloud storage creates the file/version record and PUTs the bytes; local storage uses one multipart request. The file is immediately part of the object tree—there is no DMS registration step. |
88
+ | `blocks data files *` | Full storage object-tree surface: file get/upload/metadata/delete/version/copy/move/rename, directory CRUD/move, cursor-paginated list/search/shared/trash, restore/purge, and access policy/share/inheritance commands. Delete defaults to trash; pass `--permanent` explicitly to remove data. Run `blocks --help` for flags. |
89
89
  | `blocks localization validate --module <name> --language <culture> [--file <path>] [--json]` | Validate a local i18n JSON dictionary. Supports nested JSON input and checks the flattened key/value set locally. |
90
90
  | `blocks localization push --module <name> --language <culture> [--file <path>] [--route <route>] [--context <text>] [--dry-run] [--yes] [--json]` | Create or update Localization keys from local i18n JSON via `/localization/v4/Key/SaveKeys`. Creates the module first through `/localization/v4/Module/Save` when missing. |
91
91
  | `blocks localization pull --module <name> --language <culture> [--out <path>] [--json]` | Download published cloud localization via `/localization/v4/Key/GetCloudUilmFile` and write a local JSON dictionary. |
@@ -95,9 +95,9 @@ Global options available on every command:
95
95
  | `blocks release status <buildId> [--json]` | Read Release build status by build id. Read-only. |
96
96
  | `blocks release builds list [repoId] [--repo-id <repoId>] [--json]` | List Release build details for a repository. When `repoId` is omitted, resolves it from the selected project's linked repo assets - auto-picked if there's exactly one, otherwise you're prompted to choose. Read-only. |
97
97
  | `blocks release builds get <buildId> [--json]` | Alias for `release status`. Read-only. |
98
- | `blocks new web <name> [--app-domain <domain>] [--client-id <oidcClientId>] [--x-blocks-key <tenantId>] [--blocks-api-url <url>] [--oidc-url <url>]` | Create a Vite React starter app that talks to Blocks exclusively through `@seliseblocks/client` (a single `createBlocksClient()` instance) using the SDK hosted IdP flow: `blocksClient.auth.idp.redirectToProvider()` on login click and `blocksClient.auth.idp.callback()` on `/login/callback`. Includes route guards, auto-refresh through `auth.oidc.refreshToken()`, live `auth`/`iam`/`data`/`localization` SDK examples, environment config, and safe `.gitignore` defaults. Uses the selected project (see `use`) unless `--x-blocks-key` overrides it. `--app-domain` and `--client-id` are resolved from the project record when omitted: the domain auto-picks if the project has exactly one, otherwise you're prompted to choose; the OIDC client is picked from the project's existing clients, or you can create a minimal one (display name + redirect URI) on the spot, or skip and register one later from the portal or `auth oidc-clients save`. When `--blocks-api-url` is omitted, `new web` derives it from the app domain as `https://blocksapi.<registrable-domain>`; for example `https://dqrsf.slsblx.com` becomes `https://blocksapi.slsblx.com`. Pass a different Data/IAM/Localization/OS gateway URL explicitly only if your project uses a non-default one. `--oidc-url` defaults to `https://iam.seliseblocks.com`. |
98
+ | `blocks new web <name> [--app-domain <domain>] [--client-id <oidcClientId>] [--x-blocks-key <tenantId>] [--blocks-api-url <url>] [--oidc-url <url>]` | Create a Vite React starter app that talks to Blocks exclusively through `@seliseblocks/client` (a single `createBlocksClient()` instance) using the SDK hosted IdP flow: `blocksClient.auth.idp.redirectToProvider()` on login click and `blocksClient.auth.idp.callback()` on `/login/callback`. Includes route guards, auto-refresh through `auth.oidc.refreshToken()`, live `auth`/`iam`/`data`/`localization` SDK examples, environment config, and safe `.gitignore` defaults. Uses the selected project (see `use`) unless `--x-blocks-key` overrides it. `--app-domain` and `--client-id` are resolved from the project record when omitted: the domain auto-picks if the project has exactly one, otherwise you're prompted to choose; the OIDC client is picked from the project's existing clients, or you can create a minimal one (display name + redirect URI) on the spot, or skip and register one later from the portal or `auth oidc-clients save`. When `--blocks-api-url` is omitted, `new web` derives it from the app domain as `https://blocksapi.<registrable-domain>`; for example `https://dqrsf.slsblx.com` becomes `https://blocksapi.slsblx.com`. Pass a different Data/IAM/Localization/OS gateway URL explicitly only if your project uses a non-default one. `--oidc-url` defaults to `https://iam.seliseblocks.com`. |
99
99
  | `blocks skill list [--json]` / `skill show <name> [--json]` / `skill add <name> [--dir <path>]` | Local-only, no cloud calls: list/print the bundled `blocks-skills/*/SKILL.md` agent context docs, or copy a skill's entire directory (`SKILL.md` plus any supporting files) into `<dir>/<name>/` (default `./blocks-skills`) for use in a project outside this monorepo. `list`'s output and `show`/`add`'s "unknown skill" error both point at the full public skill catalog in case the bundled set is out of date. |
100
- | `blocks sdk client [--app-domain <domain>] [--client-id <oidcClientId>] [--x-blocks-key <tenantId>] [--blocks-api-url <url>] [--oidc-url <url>] [--json]` | Read-only: resolves this project's `@seliseblocks/client` config and prints a ready-to-paste `createBlocksClient(...)` snippet. Unlike `new web`, the API URL defaults to `https://api.seliseblocks.com` unless `--blocks-api-url` is passed. Passing both `--app-domain` and `--client-id` skips the project lookup entirely (no login required). Writes nothing - use `new web` to scaffold a full app. |
100
+ | `blocks sdk client [--app-domain <domain>] [--client-id <oidcClientId>] [--x-blocks-key <tenantId>] [--blocks-api-url <url>] [--oidc-url <url>] [--json]` | Read-only: resolves this project's `@seliseblocks/client` config and prints a ready-to-paste `createBlocksClient(...)` snippet. Unlike `new web`, the API URL defaults to `https://api.seliseblocks.com` unless `--blocks-api-url` is passed. Passing both `--app-domain` and `--client-id` skips the project lookup entirely (no login required). Writes nothing - use `new web` to scaffold a full app. |
101
101
 
102
102
  Use `--json` on commands when AI or automation needs machine-readable output. Use `--dry-run` before mutations and `--yes` only after approval.
103
103
 
@@ -166,7 +166,7 @@ Localization dictionaries are not created by `init` - the default path is `block
166
166
  ## Boundaries
167
167
 
168
168
  - `iam me` reads the CLI operator's own account identity; every other `iam *`, `mfa *`, `auth idp *`/`auth config *`/`auth client-credentials *`/`auth oidc-clients *`, `mail *`, `notification *`, `notifier *`, `secrets *`, and `storage config *` command is project-scoped and requires a selected project (`blocks use <tenantId>`) plus an impersonated project token - none of them ever run against the account token.
169
- - Data covers the tenant data-source configuration, schema/rules/reload/validate, field-level validation rules, and the DMS/storage (`data files *`) surface. Prefer the composed `data sync` and `data files upload` over their individual steps.
169
+ - Data covers tenant data-source configuration, schema/rules/reload/validate, field-level validation rules, and the storage object tree (`data files *`). Prefer the composed `data sync` and `data files upload` workflows.
170
170
  - Localization covers dictionary validate/pull/push plus the full raw `/localization/v4/*` API surface (assistant, config, glossary, key, language, module). Prefer `localization key translate-and-export` over running translate/generate/export by hand.
171
171
  - Release covers deploy trigger and build status/read commands only.
172
172
  - No direct artifact upload unless Blocks Release adds a confirmed artifact upload API.
@@ -1 +1,8 @@
1
+ /**
2
+ * The endpoint URLs are not decoration: `GET /iam/v4/idp/initiate` builds the
3
+ * provider authorize URL from the stored `authorizationUrl` and falls back to an
4
+ * empty base when it is missing, so a provider saved without it hands the browser
5
+ * a bare query string. Apple-only fields (teamId/keyId/privateKey/appleAudience)
6
+ * stay in --body/--file so no private key lands in shell history.
7
+ */
1
8
  export declare function authIdpCreate(argv: string[]): Promise<void>;
@@ -1,25 +1,44 @@
1
- import { booleanFlag, stringFlag } from "../../../lib/args.js";
1
+ import { booleanFlag, optionalBooleanFlag, stringFlag } from "../../../lib/args.js";
2
2
  import { blocksRequest } from "../../../lib/api.js";
3
3
  import { confirmMutation } from "../../../lib/confirm.js";
4
4
  import { compact, jsonBodyFlag, listFlag } from "../../../lib/json-flag.js";
5
5
  import { writeOutput } from "../../../lib/output.js";
6
6
  import { requestContext } from "../../../lib/request-context.js";
7
7
  import { parseCommand, selectedProject } from "../../../lib/workspace.js";
8
+ /**
9
+ * The endpoint URLs are not decoration: `GET /iam/v4/idp/initiate` builds the
10
+ * provider authorize URL from the stored `authorizationUrl` and falls back to an
11
+ * empty base when it is missing, so a provider saved without it hands the browser
12
+ * a bare query string. Apple-only fields (teamId/keyId/privateKey/appleAudience)
13
+ * stay in --body/--file so no private key lands in shell history.
14
+ */
8
15
  export async function authIdpCreate(argv) {
9
16
  const { flags } = parseCommand(argv);
10
17
  const body = {
11
18
  ...(await jsonBodyFlag(flags)),
12
19
  ...compact({
20
+ authorizationUrl: stringFlag(flags, "authorization-url") || undefined,
13
21
  clientId: stringFlag(flags, "client-id") || undefined,
14
22
  clientSecret: stringFlag(flags, "client-secret") || undefined,
15
23
  displayName: stringFlag(flags, "display-name") || undefined,
16
- isActive: booleanFlag(flags, "active") || undefined,
24
+ grantTypes: listFlag(flags, "grant-types"),
25
+ icon: stringFlag(flags, "icon") || undefined,
26
+ initialPermissions: listFlag(flags, "initial-permissions"),
27
+ initialRoles: listFlag(flags, "initial-roles"),
28
+ isActive: optionalBooleanFlag(flags, "active"),
17
29
  issuer: stringFlag(flags, "issuer") || undefined,
30
+ jwksUri: stringFlag(flags, "jwks-uri") || undefined,
18
31
  protocol: stringFlag(flags, "protocol") || undefined,
19
32
  provider: stringFlag(flags, "provider") || undefined,
20
33
  providerType: stringFlag(flags, "provider-type") || undefined,
21
34
  redirectUris: listFlag(flags, "redirect-uris"),
22
- scope: stringFlag(flags, "scope") || undefined
35
+ requirePkce: optionalBooleanFlag(flags, "require-pkce"),
36
+ responseType: stringFlag(flags, "response-type") || undefined,
37
+ scope: stringFlag(flags, "scope") || undefined,
38
+ tokenEndpointAuthMethod: stringFlag(flags, "token-endpoint-auth-method") || undefined,
39
+ tokenUrl: stringFlag(flags, "token-url") || undefined,
40
+ userInfoUrl: stringFlag(flags, "user-info-url") || undefined,
41
+ wellKnownUrl: stringFlag(flags, "well-known-url") || undefined
23
42
  })
24
43
  };
25
44
  if (!body.provider || !body.providerType || !body.protocol || !body.clientId) {
@@ -3,5 +3,12 @@
3
3
  * IAM requires them to echo the existing value when supplied, so only pass them
4
4
  * via --provider/--provider-type/--protocol/--client-id if you're re-sending the
5
5
  * current configuration alongside other field changes.
6
+ *
7
+ * This is also the only endpoint that can set the OIDC endpoint URLs: the create
8
+ * endpoint silently drops authorizationUrl/tokenUrl/userInfoUrl, and a provider
9
+ * auto-registered from an OIDC client (--register-as-identity-provider) never has
10
+ * them. `GET /iam/v4/idp/initiate` needs authorizationUrl, so a freshly created
11
+ * provider has to be patched here before hosted login works. Apple-only fields
12
+ * stay in --body/--file so no private key lands in shell history.
6
13
  */
7
14
  export declare function authIdpUpdate(argv: string[]): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { booleanFlag, stringFlag } from "../../../lib/args.js";
1
+ import { booleanFlag, optionalBooleanFlag, stringFlag } from "../../../lib/args.js";
2
2
  import { blocksRequest } from "../../../lib/api.js";
3
3
  import { confirmMutation } from "../../../lib/confirm.js";
4
4
  import { compact, jsonBodyFlag, listFlag } from "../../../lib/json-flag.js";
@@ -10,6 +10,13 @@ import { parseCommand, selectedProject } from "../../../lib/workspace.js";
10
10
  * IAM requires them to echo the existing value when supplied, so only pass them
11
11
  * via --provider/--provider-type/--protocol/--client-id if you're re-sending the
12
12
  * current configuration alongside other field changes.
13
+ *
14
+ * This is also the only endpoint that can set the OIDC endpoint URLs: the create
15
+ * endpoint silently drops authorizationUrl/tokenUrl/userInfoUrl, and a provider
16
+ * auto-registered from an OIDC client (--register-as-identity-provider) never has
17
+ * them. `GET /iam/v4/idp/initiate` needs authorizationUrl, so a freshly created
18
+ * provider has to be patched here before hosted login works. Apple-only fields
19
+ * stay in --body/--file so no private key lands in shell history.
13
20
  */
14
21
  export async function authIdpUpdate(argv) {
15
22
  const { args, flags } = parseCommand(argv);
@@ -17,15 +24,27 @@ export async function authIdpUpdate(argv) {
17
24
  const body = {
18
25
  ...(await jsonBodyFlag(flags)),
19
26
  ...compact({
27
+ authorizationUrl: stringFlag(flags, "authorization-url") || undefined,
20
28
  clientId: stringFlag(flags, "client-id") || undefined,
21
29
  displayName: stringFlag(flags, "display-name") || undefined,
22
- isActive: booleanFlag(flags, "active") || undefined,
30
+ grantTypes: listFlag(flags, "grant-types"),
31
+ icon: stringFlag(flags, "icon") || undefined,
32
+ initialPermissions: listFlag(flags, "initial-permissions"),
33
+ initialRoles: listFlag(flags, "initial-roles"),
34
+ isActive: optionalBooleanFlag(flags, "active"),
23
35
  issuer: stringFlag(flags, "issuer") || undefined,
36
+ jwksUri: stringFlag(flags, "jwks-uri") || undefined,
24
37
  protocol: stringFlag(flags, "protocol") || undefined,
25
38
  provider: stringFlag(flags, "provider") || undefined,
26
39
  providerType: stringFlag(flags, "provider-type") || undefined,
27
40
  redirectUris: listFlag(flags, "redirect-uris"),
28
- scope: stringFlag(flags, "scope") || undefined
41
+ requirePkce: optionalBooleanFlag(flags, "require-pkce"),
42
+ responseType: stringFlag(flags, "response-type") || undefined,
43
+ scope: stringFlag(flags, "scope") || undefined,
44
+ tokenEndpointAuthMethod: stringFlag(flags, "token-endpoint-auth-method") || undefined,
45
+ tokenUrl: stringFlag(flags, "token-url") || undefined,
46
+ userInfoUrl: stringFlag(flags, "user-info-url") || undefined,
47
+ wellKnownUrl: stringFlag(flags, "well-known-url") || undefined
29
48
  })
30
49
  };
31
50
  if (booleanFlag(flags, "dry-run")) {
@@ -8,17 +8,18 @@ import { parseCommand, selectedProject } from "../../../lib/workspace.js";
8
8
  export async function dataFilesDelete(argv) {
9
9
  const { args, flags } = parseCommand(argv);
10
10
  const fileId = args[0] || stringFlag(flags, "file-id", { required: true });
11
- const body = { fileId, ...compact({
11
+ const permanent = booleanFlag(flags, "permanent");
12
+ const body = { fileId, permanent, ...compact({
12
13
  configurationName: stringFlag(flags, "configuration-name") || undefined,
13
14
  eventQueueName: stringFlag(flags, "event-queue-name") || undefined
14
15
  }) };
15
16
  if (booleanFlag(flags, "dry-run")) {
16
- writeOutput({ dryRun: true, endpoint: "/data/v4/Files/DeleteFile", request: body }, flags);
17
+ writeOutput({ dryRun: true, endpoint: "/data/v4/files/delete-file", request: body }, flags);
17
18
  return;
18
19
  }
19
- await confirmMutation(flags, `Delete file '${fileId}'.`);
20
+ await confirmMutation(flags, `${permanent ? "Permanently delete" : "Move"} file '${fileId}'${permanent ? "" : " to trash"}.`);
20
21
  const projectKey = await selectedProject(flags);
21
- const result = await blocksRequest("/data/v4/Files/DeleteFile", {
22
+ const result = await blocksRequest("/data/v4/files/delete-file", {
22
23
  body,
23
24
  impersonatedProjectAuth: true,
24
25
  ...requestContext(flags),
@@ -14,7 +14,7 @@ export async function dataFilesGetMany(argv) {
14
14
  fileIds
15
15
  });
16
16
  const projectKey = await selectedProject(flags);
17
- const result = await blocksRequest("/data/v4/Files/GetFiles", {
17
+ const result = await blocksRequest("/data/v4/files/get-files", {
18
18
  body,
19
19
  impersonatedProjectAuth: true,
20
20
  ...requestContext(flags),
@@ -7,7 +7,7 @@ export async function dataFilesGet(argv) {
7
7
  const { args, flags } = parseCommand(argv);
8
8
  const fileId = args[0] || stringFlag(flags, "file-id", { required: true });
9
9
  const projectKey = await selectedProject(flags);
10
- const result = await blocksRequest("/data/v4/Files/GetFile", {
10
+ const result = await blocksRequest("/data/v4/files/get-file", {
11
11
  impersonatedProjectAuth: true,
12
12
  ...requestContext(flags),
13
13
  projectTenantId: projectKey,
@@ -20,7 +20,7 @@ export async function dataFilesInfo(argv) {
20
20
  }
21
21
  };
22
22
  const projectKey = await selectedProject(flags);
23
- const result = await blocksRequest("/data/v4/Files/GetFilesInfo", {
23
+ const result = await blocksRequest("/data/v4/files/get-files-info", {
24
24
  body,
25
25
  impersonatedProjectAuth: true,
26
26
  ...requestContext(flags),
@@ -0,0 +1,23 @@
1
+ export declare function dataFilesList(argv: string[]): Promise<void>;
2
+ export declare function dataFilesSearch(argv: string[]): Promise<void>;
3
+ export declare function dataFilesTrash(argv: string[]): Promise<void>;
4
+ export declare function dataFilesShared(argv: string[]): Promise<void>;
5
+ export declare function dataFilesRestore(argv: string[]): Promise<void>;
6
+ export declare function dataFilesPurge(argv: string[]): Promise<void>;
7
+ export declare function dataFilesDirectoryCreate(argv: string[]): Promise<void>;
8
+ export declare function dataFilesDirectoryGet(argv: string[]): Promise<void>;
9
+ export declare function dataFilesDirectoryUpdate(argv: string[]): Promise<void>;
10
+ export declare function dataFilesDirectoryDelete(argv: string[]): Promise<void>;
11
+ export declare function dataFilesDirectoryMove(argv: string[]): Promise<void>;
12
+ export declare function dataFilesVersions(argv: string[]): Promise<void>;
13
+ export declare function dataFilesCreateVersion(argv: string[]): Promise<void>;
14
+ export declare function dataFilesCopy(argv: string[]): Promise<void>;
15
+ export declare function dataFilesMove(argv: string[]): Promise<void>;
16
+ export declare function dataFilesRename(argv: string[]): Promise<void>;
17
+ export declare function dataFilesAccessList(argv: string[]): Promise<void>;
18
+ export declare function dataFilesAccessGrant(argv: string[]): Promise<void>;
19
+ export declare function dataFilesAccessUpdate(argv: string[]): Promise<void>;
20
+ export declare function dataFilesAccessRevoke(argv: string[]): Promise<void>;
21
+ export declare function dataFilesAccessResolve(argv: string[]): Promise<void>;
22
+ export declare function dataFilesInheritance(argv: string[]): Promise<void>;
23
+ export declare function dataFilesShare(argv: string[]): Promise<void>;
@@ -0,0 +1,238 @@
1
+ import { booleanFlag, optionalBooleanFlag, optionalIntegerFlag, stringFlag } from "../../../lib/args.js";
2
+ import { blocksRequest } from "../../../lib/api.js";
3
+ import { confirmMutation } from "../../../lib/confirm.js";
4
+ import { compact, jsonBodyFlag, listFlag } from "../../../lib/json-flag.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
+ export async function dataFilesList(argv) {
9
+ const { flags } = parseCommand(argv);
10
+ await read("/data/v4/objects/get-objects", flags, compact({
11
+ cursor: stringFlag(flags, "cursor") || undefined,
12
+ limit: optionalIntegerFlag(flags, "limit"),
13
+ moduleName: optionalIntegerFlag(flags, "module-name"),
14
+ parentDirectoryId: parentId(flags) || undefined,
15
+ search: stringFlag(flags, "search") || undefined,
16
+ type: stringFlag(flags, "type") || undefined
17
+ }));
18
+ }
19
+ export async function dataFilesSearch(argv) {
20
+ const { args, flags } = parseCommand(argv);
21
+ await read("/data/v4/objects/search-objects", flags, compact({
22
+ cursor: stringFlag(flags, "cursor") || undefined,
23
+ directoryId: stringFlag(flags, "directory-id") || undefined,
24
+ limit: optionalIntegerFlag(flags, "limit"),
25
+ query: args[0] || stringFlag(flags, "query", { required: true }),
26
+ type: stringFlag(flags, "type") || undefined
27
+ }));
28
+ }
29
+ export async function dataFilesTrash(argv) {
30
+ const { flags } = parseCommand(argv);
31
+ await read("/data/v4/objects/get-trash", flags, pageQuery(flags));
32
+ }
33
+ export async function dataFilesShared(argv) {
34
+ const { flags } = parseCommand(argv);
35
+ await read("/data/v4/objects/get-shared-objects", flags, pageQuery(flags));
36
+ }
37
+ export async function dataFilesRestore(argv) {
38
+ const { args, flags } = parseCommand(argv);
39
+ const resourceId = resourceArg(args, flags);
40
+ await mutate("/data/v4/objects/restore-from-trash", flags, { resourceId }, `Restore storage object '${resourceId}' from trash.`);
41
+ }
42
+ export async function dataFilesPurge(argv) {
43
+ const { args, flags } = parseCommand(argv);
44
+ const resourceId = resourceArg(args, flags);
45
+ await mutate("/data/v4/objects/delete-from-trash", flags, { resourceId }, `Permanently delete storage object '${resourceId}' from trash.`);
46
+ }
47
+ export async function dataFilesDirectoryCreate(argv) {
48
+ const { args, flags } = parseCommand(argv);
49
+ const base = await jsonBodyFlag(flags);
50
+ const name = args[0] || stringFlag(flags, "name") || String(base.name ?? "");
51
+ if (!name)
52
+ throw new Error("Provide a directory name as the first argument or --name.");
53
+ const body = { ...base, ...compact({
54
+ allowedFileExtensions: listFlag(flags, "allowed-extensions"),
55
+ configurationName: stringFlag(flags, "configuration-name") || undefined,
56
+ description: stringFlag(flags, "description") || undefined,
57
+ moduleName: optionalIntegerFlag(flags, "module-name"),
58
+ name,
59
+ parentDirectoryId: parentId(flags) || undefined
60
+ }) };
61
+ await mutate("/data/v4/directory/create-directory", flags, body, `Create storage directory '${name}'.`);
62
+ }
63
+ export async function dataFilesDirectoryGet(argv) {
64
+ const { args, flags } = parseCommand(argv);
65
+ const directoryId = directoryArg(args, flags);
66
+ await read("/data/v4/directory/get-directory", flags, { directoryId });
67
+ }
68
+ export async function dataFilesDirectoryUpdate(argv) {
69
+ const { args, flags } = parseCommand(argv);
70
+ const directoryId = directoryArg(args, flags);
71
+ const body = { ...(await jsonBodyFlag(flags)), ...compact({
72
+ description: stringFlag(flags, "description") || undefined,
73
+ directoryId,
74
+ name: stringFlag(flags, "name") || undefined
75
+ }) };
76
+ await mutate("/data/v4/directory/update-directory", flags, body, `Update storage directory '${directoryId}'.`);
77
+ }
78
+ export async function dataFilesDirectoryDelete(argv) {
79
+ const { args, flags } = parseCommand(argv);
80
+ const directoryId = directoryArg(args, flags);
81
+ const permanent = booleanFlag(flags, "permanent");
82
+ await mutate("/data/v4/directory/delete-directory", flags, { directoryId, permanent }, `${permanent ? "Permanently delete" : "Move"} storage directory '${directoryId}'${permanent ? "" : " to trash"}.`);
83
+ }
84
+ export async function dataFilesDirectoryMove(argv) {
85
+ const { args, flags } = parseCommand(argv);
86
+ const directoryId = directoryArg(args, flags);
87
+ const targetDirectoryId = stringFlag(flags, "target-directory-id") || stringFlag(flags, "target-id");
88
+ await mutate("/data/v4/directory/move-directory", flags, { directoryId, targetDirectoryId }, `Move storage directory '${directoryId}'.`);
89
+ }
90
+ export async function dataFilesVersions(argv) {
91
+ const { args, flags } = parseCommand(argv);
92
+ await read("/data/v4/files/get-file-versions", flags, compact({
93
+ cursor: stringFlag(flags, "cursor") || undefined,
94
+ fileId: fileArg(args, flags),
95
+ limit: optionalIntegerFlag(flags, "limit")
96
+ }));
97
+ }
98
+ export async function dataFilesCreateVersion(argv) {
99
+ const { args, flags } = parseCommand(argv);
100
+ const fileId = fileArg(args, flags);
101
+ await mutate("/data/v4/files/create-file-version", flags, compact({
102
+ configurationName: stringFlag(flags, "configuration-name") || undefined,
103
+ fileId
104
+ }), `Create a new upload version for file '${fileId}'.`);
105
+ }
106
+ export async function dataFilesCopy(argv) {
107
+ const { args, flags } = parseCommand(argv);
108
+ const fileId = fileArg(args, flags);
109
+ const targetDirectoryId = stringFlag(flags, "target-directory-id", { required: true });
110
+ await mutate("/data/v4/files/copy-file", flags, {
111
+ copyAccessPolicies: booleanFlag(flags, "copy-access-policies"), fileId, targetDirectoryId
112
+ }, `Copy file '${fileId}' to directory '${targetDirectoryId}'.`);
113
+ }
114
+ export async function dataFilesMove(argv) {
115
+ const { args, flags } = parseCommand(argv);
116
+ const fileId = fileArg(args, flags);
117
+ const targetDirectoryId = stringFlag(flags, "target-directory-id", { required: true });
118
+ await mutate("/data/v4/files/move-file", flags, { fileId, targetDirectoryId }, `Move file '${fileId}' to directory '${targetDirectoryId}'.`);
119
+ }
120
+ export async function dataFilesRename(argv) {
121
+ const { args, flags } = parseCommand(argv);
122
+ const fileId = fileArg(args, flags);
123
+ const name = stringFlag(flags, "name", { required: true });
124
+ await mutate("/data/v4/files/rename-file", flags, { fileId, name }, `Rename file '${fileId}' to '${name}'.`);
125
+ }
126
+ export async function dataFilesAccessList(argv) {
127
+ const { args, flags } = parseCommand(argv);
128
+ await read("/data/v4/objects/get-access-policies", flags, { resourceId: resourceArg(args, flags) });
129
+ }
130
+ export async function dataFilesAccessGrant(argv) {
131
+ await accessMutation(argv, "/data/v4/objects/grant-access", "Grant access to");
132
+ }
133
+ export async function dataFilesAccessUpdate(argv) {
134
+ await accessMutation(argv, "/data/v4/objects/update-access-policy", "Update access on", true);
135
+ }
136
+ export async function dataFilesAccessRevoke(argv) {
137
+ const { args, flags } = parseCommand(argv);
138
+ const resourceId = resourceArg(args, flags);
139
+ const policyItemId = stringFlag(flags, "policy-id", { required: true });
140
+ await mutate("/data/v4/objects/revoke-access-policy", flags, { policyItemId, resourceId }, `Revoke access policy '${policyItemId}' from '${resourceId}'.`);
141
+ }
142
+ export async function dataFilesAccessResolve(argv) {
143
+ const { args, flags } = parseCommand(argv);
144
+ await read("/data/v4/objects/resolve-access", flags, { resourceId: resourceArg(args, flags) });
145
+ }
146
+ export async function dataFilesInheritance(argv) {
147
+ const { args, flags } = parseCommand(argv);
148
+ const base = await jsonBodyFlag(flags);
149
+ const value = optionalBooleanFlag(flags, "enabled");
150
+ const inheritsParentAccess = value ?? base.inheritsParentAccess;
151
+ if (typeof inheritsParentAccess !== "boolean")
152
+ throw new Error("Provide --enabled=true|false.");
153
+ const resourceId = resourceArg(args, flags, base);
154
+ await mutate("/data/v4/objects/toggle-inheritance", flags, { ...base, inheritsParentAccess, resourceId }, `Set inheritance on '${resourceId}' to ${inheritsParentAccess}.`);
155
+ }
156
+ export async function dataFilesShare(argv) {
157
+ const { args, flags } = parseCommand(argv);
158
+ const base = await jsonBodyFlag(flags);
159
+ const resourceId = resourceArg(args, flags, base);
160
+ const body = accessBody(base, flags, resourceId);
161
+ delete body.effect;
162
+ delete body.policyItemId;
163
+ await mutate("/data/v4/objects/share-object", flags, body, `Share storage object '${resourceId}'.`);
164
+ }
165
+ async function accessMutation(argv, endpoint, action, update = false) {
166
+ const { args, flags } = parseCommand(argv);
167
+ const base = await jsonBodyFlag(flags);
168
+ const resourceId = resourceArg(args, flags, base);
169
+ const body = accessBody(base, flags, resourceId);
170
+ if (update && !body.policyItemId)
171
+ throw new Error("Provide --policy-id when updating an access policy.");
172
+ await mutate(endpoint, flags, body, `${action} storage object '${resourceId}'.`);
173
+ }
174
+ function accessBody(base, flags, resourceId) {
175
+ const body = { ...base, ...compact({
176
+ effect: stringFlag(flags, "effect") || undefined,
177
+ expiresAt: stringFlag(flags, "expires-at") || undefined,
178
+ permission: stringFlag(flags, "permission") || undefined,
179
+ policyItemId: stringFlag(flags, "policy-id") || undefined,
180
+ principalId: stringFlag(flags, "principal-id") || undefined,
181
+ principalType: stringFlag(flags, "principal-type") || undefined,
182
+ priority: optionalIntegerFlag(flags, "priority"),
183
+ resourceId,
184
+ resourceType: stringFlag(flags, "resource-type") || undefined
185
+ }) };
186
+ for (const field of ["permission", "principalType", "resourceType"]) {
187
+ if (!body[field])
188
+ throw new Error(`Provide --${field.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}.`);
189
+ }
190
+ return body;
191
+ }
192
+ async function read(endpoint, flags, query) {
193
+ const projectKey = await selectedProject(flags);
194
+ const result = await blocksRequest(endpoint, {
195
+ impersonatedProjectAuth: true,
196
+ ...requestContext(flags),
197
+ projectTenantId: projectKey,
198
+ query
199
+ });
200
+ writeOutput(result, flags);
201
+ }
202
+ async function mutate(endpoint, flags, body, prompt) {
203
+ if (booleanFlag(flags, "dry-run")) {
204
+ writeOutput({ dryRun: true, endpoint, request: body }, flags);
205
+ return;
206
+ }
207
+ await confirmMutation(flags, prompt);
208
+ const projectKey = await selectedProject(flags);
209
+ const result = await blocksRequest(endpoint, {
210
+ body,
211
+ impersonatedProjectAuth: true,
212
+ ...requestContext(flags),
213
+ projectTenantId: projectKey
214
+ });
215
+ writeOutput(result, flags);
216
+ }
217
+ function pageQuery(flags) {
218
+ return compact({
219
+ cursor: stringFlag(flags, "cursor") || undefined,
220
+ limit: optionalIntegerFlag(flags, "limit"),
221
+ type: stringFlag(flags, "type") || undefined
222
+ });
223
+ }
224
+ function parentId(flags) {
225
+ return stringFlag(flags, "parent-directory-id") || stringFlag(flags, "parent-id");
226
+ }
227
+ function resourceArg(args, flags, base = {}) {
228
+ const value = args[0] || stringFlag(flags, "resource-id") || String(base.resourceId ?? "");
229
+ if (!value)
230
+ throw new Error("Provide a resource id as the first argument or --resource-id.");
231
+ return value;
232
+ }
233
+ function directoryArg(args, flags) {
234
+ return args[0] || stringFlag(flags, "directory-id", { required: true });
235
+ }
236
+ function fileArg(args, flags) {
237
+ return args[0] || stringFlag(flags, "file-id", { required: true });
238
+ }
@@ -1,5 +1,6 @@
1
- import { optionalIntegerFlag, stringFlag } from "../../../lib/args.js";
1
+ import { booleanFlag, optionalIntegerFlag, stringFlag } from "../../../lib/args.js";
2
2
  import { blocksRequest } from "../../../lib/api.js";
3
+ import { confirmMutation } from "../../../lib/confirm.js";
3
4
  import { compact, jsonBodyFlag } from "../../../lib/json-flag.js";
4
5
  import { writeOutput } from "../../../lib/output.js";
5
6
  import { requestContext } from "../../../lib/request-context.js";
@@ -16,6 +17,7 @@ export async function dataFilesPresignedUploadUrl(argv) {
16
17
  ...compact({
17
18
  accessModifier: stringFlag(flags, "access-modifier") || undefined,
18
19
  configurationName: stringFlag(flags, "configuration-name") || undefined,
20
+ itemId: stringFlag(flags, "item-id") || undefined,
19
21
  metaData: stringFlag(flags, "meta-data") || undefined,
20
22
  moduleName: optionalIntegerFlag(flags, "module-name"),
21
23
  name: stringFlag(flags, "name", { required: true }),
@@ -23,8 +25,13 @@ export async function dataFilesPresignedUploadUrl(argv) {
23
25
  tags: stringFlag(flags, "tags") || undefined
24
26
  })
25
27
  };
28
+ if (booleanFlag(flags, "dry-run")) {
29
+ writeOutput({ dryRun: true, endpoint: "/data/v4/files/get-pre-signed-url-for-upload", request: body }, flags);
30
+ return;
31
+ }
32
+ await confirmMutation(flags, `Create file metadata and an upload URL for '${body.name}'.`);
26
33
  const projectKey = await selectedProject(flags);
27
- const result = await blocksRequest("/data/v4/Files/GetPreSignedUrlForUpload", {
34
+ const result = await blocksRequest("/data/v4/files/get-pre-signed-url-for-upload", {
28
35
  body,
29
36
  impersonatedProjectAuth: true,
30
37
  ...requestContext(flags),
@@ -11,12 +11,12 @@ export async function dataFilesUpdateAdditionalInfo(argv) {
11
11
  const additionalProperties = JSON.parse(additionalPropertiesRaw);
12
12
  const body = { additionalProperties, itemId };
13
13
  if (booleanFlag(flags, "dry-run")) {
14
- writeOutput({ dryRun: true, endpoint: "/data/v4/Files/UpdateFileAdditionalInfo", request: body }, flags);
14
+ writeOutput({ dryRun: true, endpoint: "/data/v4/files/update-file-additional-info", request: body }, flags);
15
15
  return;
16
16
  }
17
17
  await confirmMutation(flags, `Update additional info for file '${itemId}'.`);
18
18
  const projectKey = await selectedProject(flags);
19
- const result = await blocksRequest("/data/v4/Files/UpdateFileAdditionalInfo", {
19
+ const result = await blocksRequest("/data/v4/files/update-file-additional-info", {
20
20
  body,
21
21
  impersonatedProjectAuth: true,
22
22
  ...requestContext(flags),