diff --git a/README.md b/README.md index 919e63a..c2b0c01 100644 --- a/README.md +++ b/README.md @@ -77,9 +77,17 @@ norbix hub email templates get # email/sms/push route into notif ``` The first positional value becomes `id`; `--field value` flags become request -fields. Destructive verbs (delete, remove, rotate, ...) ask for confirmation -unless `--yes`. Add `--dry-run` to preview the exact SDK call. -| `norbix autocomplete` | Set up shell tab-completion (bash/zsh) | +fields, typed from the SDK's request type (`norbix hub --help` lists +every method with its fields; `--json` returns them as data). Force a type +with `--field:str`, `--field:num`, `--field:bool` or `--field:json`, or pass +the whole request with `--body ''`. Destructive verbs (delete, remove, +stop, disable, block, regenerate, rotate) ask for confirmation unless +`--yes` — and exit 3 without a terminal. Add `--dry-run` to preview the exact +HTTP request. + +`norbix schema --json` describes every command (args, flags, examples, +destructive, dry-run) for scripts and coding agents; `norbix autocomplete` +sets up shell tab-completion (bash/zsh). Run `norbix --help` for flags and examples. diff --git a/docs/tasks/agent-native-cli.md b/docs/tasks/agent-native-cli.md index 0063219..e207294 100644 --- a/docs/tasks/agent-native-cli.md +++ b/docs/tasks/agent-native-cli.md @@ -367,7 +367,7 @@ code (parallel work), colour, new runtime dependencies. `login`/`configure` guarded, "Cancelled" → exit 9, tests — **done** 2. PR 2 — discovery: `norbix schema`, `--dry-run` on every mutating command, realistic `--help` examples, hub/api parser fixes (`--body`, typed - fields, boolean traps) — todo + fields, boolean traps) — **done** 3. PR 3 — startup (`oclif manifest` in build, lazy prompts), docs (`agent-contract.md`, `AGENTS.md`), `norbix ai init`, README section — todo @@ -389,6 +389,14 @@ code (parallel work), colour, new runtime dependencies. | `package.json` | `oclif.hooks` | 1 | | `tests/_cli.ts`, `tests/agent-mode.test.ts`, `tests/cli-error.test.ts` | new: child-process tests against a fake gateway; envelope unit tests | 1 | | `README.md` | "Errors and exit codes" section | 1 | +| `scripts/gen-request-fields.mjs`, `src/generated/request-fields.json` | new: request-field map generated from the installed SDK (578 methods: route, DTO, fields with kind/required); `npm run gen:fields`; copied into `dist/` by the build | 2 | +| `src/lib/request-fields.ts` | new: lazy loader + helpers for the map (no oclif import) | 2 | +| `src/lib/dispatch.ts` | two-step typing (`parseArgv` → `typeFields` with the method's kinds), `--field:str/:num/:bool/:json`, boolean flags give the next token back, disable/block destructive | 2 | +| `src/lib/namespace-command.ts`, `hub.ts`, `api.ts` | `--body` (rejects mixing with `--field`), fields in scope help and in `--json` listings, value rules text | 2 | +| `src/lib/schema.ts`, `src/commands/schema.ts` | new: `norbix schema [command] --json` (lib has no oclif import) | 2 | +| 35 mutating command files | `...BaseCommand.dryRunFlags`; explicit dry runs in `config set/unset`, `env use`, `logout`, `raw`, `files publish/unpublish` (`publicFilesRequest`) | 2 | +| every command file | realistic example values; a `--dry-run` example on every mutating command, `--yes` on every destructive one | 2 | +| `tests/schema.test.ts`, `tests/dispatch.test.ts`, `tests/agent-mode.test.ts` | schema snapshot + completeness, parser unit tests, dry-run on non-destructive / local / raw-fetch commands, `--body` | 2 | ### Findings @@ -415,9 +423,20 @@ code (parallel work), colour, new runtime dependencies. (heavy load); the before/after numbers in the report use the best of several runs. +- The SDK's `hub2.dtos.d.ts` names some request DTOs without the `Request` + suffix (`DeleteSchedulerTask`) — the generator keys on the JSDoc + "Request DTO:" line in `dist/index.js`, so it does not matter. +- `files upload` runs three calls (signed URL, PUT, commit); `--dry-run` + stops at the first SDK call and shows that one. +- `looksLikeId`: "v2", "PROD", "SMS" are still values, not words. Documented + in the scope help ("words are lowercase letters") instead of changed — + every current method is reachable with lowercase words. + ### Rejected / moved out -- (none yet) +- Parser: changing `looksLikeId` (uppercase = value) was part of the ask + (§2.5); kept and documented, because an uppercase token is almost always + an environment name or an id, and lowercase words reach every method. ### Needs you diff --git a/package.json b/package.json index 85fc8c1..25c399b 100644 --- a/package.json +++ b/package.json @@ -36,14 +36,15 @@ ], "scripts": { "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"", - "build": "npm run clean && tsc", + "build": "npm run clean && tsc && node -e \"require('node:fs').cpSync('src/generated','dist/generated',{recursive:true})\"", "typecheck": "tsc --noEmit", "dev": "npm run build && node ./bin/run.js", "prepack": "npm run build && oclif manifest", "postpack": "node -e \"require('node:fs').rmSync('oclif.manifest.json',{force:true})\"", "test": "vitest run", "test:watch": "vitest", - "pretest": "npm run build" + "pretest": "npm run build", + "gen:fields": "node scripts/gen-request-fields.mjs" }, "dependencies": { "@inquirer/prompts": "^8.7.2", diff --git a/scripts/gen-request-fields.mjs b/scripts/gen-request-fields.mjs new file mode 100644 index 0000000..444f197 --- /dev/null +++ b/scripts/gen-request-fields.mjs @@ -0,0 +1,180 @@ +#!/usr/bin/env node +/** + * Generates src/generated/request-fields.json from the installed + * `@norbix.ai/ts`: for every SDK method of `api.*` and `hub.*`, the HTTP + * method, the path, the request DTO and its fields (name, type, required). + * + * `norbix hub --help` lists the fields from it, and the plain-word + * parser uses it to type values (a string field stays "0042", a boolean flag + * does not swallow the next word). Run `npm run gen:fields` after bumping the + * SDK and commit the result. + * + * Sources (no runtime cost — pure text parsing at build time): + * dist/index.js method → {target, path, method, dto} (JSDoc + send call) + * dist/types/*.dtos.d.ts dto → fields (class bodies) + */ +import {readFileSync, writeFileSync, mkdirSync} from 'node:fs' +import {createRequire} from 'node:module' +import {dirname, join} from 'node:path' +import {fileURLToPath} from 'node:url' + +const require = createRequire(import.meta.url) +const root = dirname(dirname(fileURLToPath(import.meta.url))) +// The package exports map hides package.json; resolve the entry point and walk up. +const sdkDir = dirname(dirname(require.resolve('@norbix.ai/ts'))) +const sdkVersion = JSON.parse(readFileSync(join(sdkDir, 'package.json'), 'utf8')).version +const indexJs = readFileSync(join(sdkDir, 'dist/index.js'), 'utf8') + +// ---- 1. module classes → methods ------------------------------------------- + +/** `var FooModule = class {` … up to the next top-level `var X = class`. */ +function classBlocks(source) { + const blocks = new Map() + const re = /^var (\w+) = class \{$/gm + const starts = [] + let m + while ((m = re.exec(source))) starts.push({name: m[1], at: m.index}) + for (let i = 0; i < starts.length; i++) { + const end = i + 1 < starts.length ? starts[i + 1].at : source.length + blocks.set(starts[i].name, source.slice(starts[i].at, end)) + } + + return blocks +} + +const METHOD_RE = + /\/\*\*\s*\n\s*\* (GET|POST|PUT|PATCH|DELETE) (\S+)\s*\n\s*\* Request DTO: (\w+)\s*\n\s*\*\/\s*\n\s*(\w+) = \(request = \{\}, options = \{\}\) => \{\s*\n\s*return this\.transport\.send\(\{\s*\n\s*target: "(api|hub)",/g + +function methodsOf(block) { + const out = {} + let m + while ((m = METHOD_RE.exec(block))) { + const [, http, path, dto, name, target] = m + out[name] = {http, path, dto, target} + } + + return out +} + +/** `this.membership = new MembershipModule(transport)` inside a namespace class. */ +function modulesOf(block) { + const out = {} + const re = /this\.(\w+) = new (\w+)\(transport\)/g + let m + while ((m = re.exec(block))) out[m[1]] = m[2] + return out +} + +const blocks = classBlocks(indexJs) +const namespaces = {api: modulesOf(blocks.get('ApiNamespace')), hub: modulesOf(blocks.get('HubNamespace'))} + +// ---- 2. DTO declarations → fields ------------------------------------------- + +function parseDts(file) { + const text = readFileSync(file, 'utf8') + const lines = text.split('\n') + const classes = new Map() + const enums = new Map() + for (let i = 0; i < lines.length; i++) { + const cls = lines[i].match(/^\s+class (\w+)(?: extends (\w+))?/) + if (cls) { + const fields = [] + for (let j = i + 1; j < lines.length; j++) { + const line = lines[j] + if (/^\s+constructor\(|^\s+\}/.test(line)) break + const f = line.match(/^\s+(\w+)(\?)?: ([^;]+);$/) + if (f) fields.push({name: f[1], type: f[3].trim(), required: f[2] === undefined}) + } + + classes.set(cls[1], {extends: cls[2], fields}) + continue + } + + const en = lines[i].match(/^\s+enum (\w+)/) + if (en) { + const values = [] + for (let j = i + 1; j < lines.length && !/^\s+\}/.test(lines[j]); j++) { + const v = lines[j].match(/=\s*"([^"]*)"/) + if (v) values.push(v[1]) + } + + enums.set(en[1], values) + } + } + + return {classes, enums} +} + +/** Request base classes carry headers / path tokens the CLI fills itself. */ +const HIDDEN_FIELDS = new Set(['cultureCode', 'timeZoneId', 'version', 'correlationId', 'projectId', 'env', 'resolvedEnv']) + +function kindOf(type, enums, classes) { + const array = type.endsWith('[]') + const base = array ? type.slice(0, -2) : type + let kind + let options + if (base === 'string') kind = 'string' + else if (base === 'number') kind = 'number' + else if (base === 'boolean') kind = 'boolean' + else if (enums.has(base)) { + kind = 'string' + options = enums.get(base) + } else if (/^(string|number|boolean) \| /.test(type)) kind = 'json' + else if (/Id$/.test(base) && classes.has(base)) kind = 'string' + else kind = 'json' + return {kind: array ? `${kind}[]` : kind, options} +} + +function fieldsOf(dto, {classes, enums}) { + const seen = new Map() + let name = dto + const chain = [] + while (name && classes.has(name)) { + chain.unshift(classes.get(name)) + name = classes.get(name).extends + } + + for (const cls of chain) { + for (const f of cls.fields) { + if (HIDDEN_FIELDS.has(f.name)) continue + const {kind, options} = kindOf(f.type, enums, classes) + const entry = {name: f.name, type: f.type, kind, required: f.required} + if (options) entry.options = options + seen.set(f.name, entry) + } + } + + return [...seen.values()] +} + +const dts = { + api: parseDts(join(sdkDir, 'dist/types/api2.dtos.d.ts')), + hub: parseDts(join(sdkDir, 'dist/types/hub2.dtos.d.ts')), +} + +// ---- 3. assemble -------------------------------------------------------------- + +const out = {sdk: `@norbix.ai/ts@${sdkVersion}`, api: {}, hub: {}} +let methodCount = 0 +let missingDto = 0 +for (const target of ['api', 'hub']) { + for (const [moduleName, className] of Object.entries(namespaces[target])) { + const block = blocks.get(className) + if (!block) continue + const methods = methodsOf(block) + const entry = {} + for (const [method, info] of Object.entries(methods)) { + const fields = dts[target].classes.has(info.dto) ? fieldsOf(info.dto, dts[target]) : null + if (!fields) missingDto++ + entry[method] = {http: info.http, path: info.path, dto: info.dto, fields: fields ?? []} + methodCount++ + } + + out[target][moduleName] = entry + } +} + +const target = join(root, 'src/generated/request-fields.json') +mkdirSync(dirname(target), {recursive: true}) +writeFileSync(target, JSON.stringify(out) + '\n') +console.log(`wrote ${target}: ${methodCount} methods (${missingDto} without a DTO declaration), sdk ${sdkVersion}`) diff --git a/src/commands/ai/llm.ts b/src/commands/ai/llm.ts index 7146b2b..c0818d6 100644 --- a/src/commands/ai/llm.ts +++ b/src/commands/ai/llm.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class AiLlm extends BaseCommand { static description = 'Show one LLM integration' - static examples = ['<%= config.bin %> ai llm 66b2f0a1...'] + static examples = ['<%= config.bin %> ai llm 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Integration ID'}), diff --git a/src/commands/ai/llm/default.ts b/src/commands/ai/llm/default.ts index e14dd1d..1f4b150 100644 --- a/src/commands/ai/llm/default.ts +++ b/src/commands/ai/llm/default.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class AiLlmDefault extends BaseCommand { static description = 'Make an LLM integration the project default' - static examples = ['<%= config.bin %> ai llm default 66b2f0a1...'] + static examples = [ + '<%= config.bin %> ai llm default 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> ai llm default 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(AiLlmDefault) const client = this.client(flags) diff --git a/src/commands/ai/llm/delete.ts b/src/commands/ai/llm/delete.ts index d69e9c0..f60ceb4 100644 --- a/src/commands/ai/llm/delete.ts +++ b/src/commands/ai/llm/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class AiLlmDelete extends BaseCommand { static description = 'Delete an LLM integration' - static examples = ['<%= config.bin %> ai llm delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> ai llm delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> ai llm delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(AiLlmDelete) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete LLM integration ${args.id}?`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete LLM integration ${args.id}?`, flags) const res = await client.hub.ai.deleteLlmIntegration({id: args.id}) diff --git a/src/commands/ai/llm/disable.ts b/src/commands/ai/llm/disable.ts index 3f6f1f0..f01e871 100644 --- a/src/commands/ai/llm/disable.ts +++ b/src/commands/ai/llm/disable.ts @@ -5,15 +5,23 @@ import {BaseCommand} from '../../../base.js' export default class AiLlmDisable extends BaseCommand { static description = 'Turn off an LLM integration' - static examples = ['<%= config.bin %> ai llm disable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> ai llm disable 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> ai llm disable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.mutatingFlags, + } + async run(): Promise { const {args, flags} = await this.parse(AiLlmDisable) const client = this.client(flags) + await this.confirmOrFail(`Turn off LLM integration ${args.id}?`, flags) const res = await client.hub.ai.disableLlmIntegration({id: args.id}) diff --git a/src/commands/ai/llm/enable.ts b/src/commands/ai/llm/enable.ts index e55b80d..4166606 100644 --- a/src/commands/ai/llm/enable.ts +++ b/src/commands/ai/llm/enable.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class AiLlmEnable extends BaseCommand { static description = 'Turn on an LLM integration' - static examples = ['<%= config.bin %> ai llm enable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> ai llm enable 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> ai llm enable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(AiLlmEnable) const client = this.client(flags) diff --git a/src/commands/ai/llm/save.ts b/src/commands/ai/llm/save.ts index 44bc40b..ff796fe 100644 --- a/src/commands/ai/llm/save.ts +++ b/src/commands/ai/llm/save.ts @@ -17,9 +17,11 @@ update an existing integration.` static examples = [ '<%= config.bin %> ai llm save --provider OpenAI --name OpenAI --model gpt-4o-mini --config @openai.json', '<%= config.bin %> ai llm save --provider Ollama --name Local --endpoint http://localhost:11434 --model llama3', + '<%= config.bin %> ai llm save --provider OpenAI --name OpenAI --model gpt-4o-mini --config @openai.json --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, provider: Flags.string({required: true, description: 'LLM provider', options: PROVIDERS}), name: Flags.string({required: true, description: 'Integration name'}), id: Flags.string({description: 'Integration ID — set it to update instead of create'}), diff --git a/src/commands/ai/llm/test.ts b/src/commands/ai/llm/test.ts index 4373c41..e5c21c9 100644 --- a/src/commands/ai/llm/test.ts +++ b/src/commands/ai/llm/test.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class AiLlmTest extends BaseCommand { static description = 'Check that an LLM integration works: a live call to the provider' - static examples = ['<%= config.bin %> ai llm test 66b2f0a1...'] + static examples = [ + '<%= config.bin %> ai llm test 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> ai llm test 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(AiLlmTest) const client = this.client(flags) diff --git a/src/commands/ai/mcp.ts b/src/commands/ai/mcp.ts index af30628..254e270 100644 --- a/src/commands/ai/mcp.ts +++ b/src/commands/ai/mcp.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class AiMcp extends BaseCommand { static description = 'Show one MCP server integration' - static examples = ['<%= config.bin %> ai mcp 66b2f0a1...'] + static examples = ['<%= config.bin %> ai mcp 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Integration ID'}), diff --git a/src/commands/ai/mcp/delete.ts b/src/commands/ai/mcp/delete.ts index 791c6fb..c70e414 100644 --- a/src/commands/ai/mcp/delete.ts +++ b/src/commands/ai/mcp/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class AiMcpDelete extends BaseCommand { static description = 'Delete an MCP server integration' - static examples = ['<%= config.bin %> ai mcp delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> ai mcp delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> ai mcp delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(AiMcpDelete) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete MCP server integration ${args.id}?`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete MCP server integration ${args.id}?`, flags) const res = await client.hub.ai.deleteMcpIntegration({id: args.id}) diff --git a/src/commands/ai/mcp/disable.ts b/src/commands/ai/mcp/disable.ts index 11f11a6..dd96ea0 100644 --- a/src/commands/ai/mcp/disable.ts +++ b/src/commands/ai/mcp/disable.ts @@ -5,15 +5,23 @@ import {BaseCommand} from '../../../base.js' export default class AiMcpDisable extends BaseCommand { static description = 'Turn off an MCP server integration' - static examples = ['<%= config.bin %> ai mcp disable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> ai mcp disable 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> ai mcp disable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.mutatingFlags, + } + async run(): Promise { const {args, flags} = await this.parse(AiMcpDisable) const client = this.client(flags) + await this.confirmOrFail(`Turn off MCP integration ${args.id}?`, flags) const res = await client.hub.ai.disableMcpIntegration({id: args.id}) diff --git a/src/commands/ai/mcp/enable.ts b/src/commands/ai/mcp/enable.ts index 2c859e4..746c609 100644 --- a/src/commands/ai/mcp/enable.ts +++ b/src/commands/ai/mcp/enable.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class AiMcpEnable extends BaseCommand { static description = 'Turn on an MCP server integration' - static examples = ['<%= config.bin %> ai mcp enable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> ai mcp enable 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> ai mcp enable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(AiMcpEnable) const client = this.client(flags) diff --git a/src/commands/ai/mcp/save.ts b/src/commands/ai/mcp/save.ts index 6ce2e4d..2408696 100644 --- a/src/commands/ai/mcp/save.ts +++ b/src/commands/ai/mcp/save.ts @@ -17,9 +17,11 @@ Pass --id to update an existing integration.` static examples = [ '<%= config.bin %> ai mcp save --provider GitHub --name GitHub --server-name GitHub --category Code --description "Issues and pull requests" --icon github --config @github.json', + '<%= config.bin %> ai mcp save --provider GitHub --name GitHub --server-name GitHub --category Code --description "Issues and pull requests" --icon github --config @github.json --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, provider: Flags.string({required: true, description: 'MCP server provider', options: PROVIDERS}), name: Flags.string({required: true, description: 'Integration name'}), id: Flags.string({description: 'Integration ID — set it to update instead of create'}), diff --git a/src/commands/ai/mcp/test.ts b/src/commands/ai/mcp/test.ts index ae42fbe..bbe826d 100644 --- a/src/commands/ai/mcp/test.ts +++ b/src/commands/ai/mcp/test.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class AiMcpTest extends BaseCommand { static description = 'Check that an MCP server integration works: connect to the server' - static examples = ['<%= config.bin %> ai mcp test 66b2f0a1...'] + static examples = [ + '<%= config.bin %> ai mcp test 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> ai mcp test 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(AiMcpTest) const client = this.client(flags) diff --git a/src/commands/ai/service-user/create.ts b/src/commands/ai/service-user/create.ts index 5aa1714..2994273 100644 --- a/src/commands/ai/service-user/create.ts +++ b/src/commands/ai/service-user/create.ts @@ -12,9 +12,11 @@ admin can change things. --env picks the environments (repeat the flag).` static examples = [ '<%= config.bin %> ai service-user create --name "Claude Code on my laptop" --reach project --rights read --env TEST', '<%= config.bin %> ai service-user create --name ci-bot --reach account --rights admin --env TEST --env PROD', + '<%= config.bin %> ai service-user create --name "Claude Code on my laptop" --reach project --rights read --env TEST --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, name: Flags.string({required: true, description: 'A name people recognise'}), reach: Flags.string({description: 'Whole account, or one project', options: ['account', 'project'], default: 'project'}), rights: Flags.string({description: 'What it may do', options: ['read', 'admin'], default: 'read'}), diff --git a/src/commands/ai/service-user/delete.ts b/src/commands/ai/service-user/delete.ts index 56d851d..6743d6d 100644 --- a/src/commands/ai/service-user/delete.ts +++ b/src/commands/ai/service-user/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class AiServiceUserDelete extends BaseCommand { static description = 'Delete an AI service user; its keys stop working at once' - static examples = ['<%= config.bin %> ai service-user delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> ai service-user delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> ai service-user delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Service user ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(AiServiceUserDelete) const client = this.client(flags, {requireProject: false}) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete AI service user ${args.id}? Its keys stop working.`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete AI service user ${args.id}? Its keys stop working.`, flags) const res = await client.hub.account.deleteAiServiceUser({id: args.id}) diff --git a/src/commands/ai/service-user/revoke-key.ts b/src/commands/ai/service-user/revoke-key.ts index 0b93d84..28dc1d9 100644 --- a/src/commands/ai/service-user/revoke-key.ts +++ b/src/commands/ai/service-user/revoke-key.ts @@ -1,12 +1,14 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class AiServiceUserRevokeKey extends BaseCommand { static description = 'Revoke one API key of an AI service user; it stops working at once' - static examples = ['<%= config.bin %> ai service-user revoke-key 66b2f0a1... key_123 --yes'] + static examples = [ + '<%= config.bin %> ai service-user revoke-key 66b2f0a1c3d4e5f6a7b8c9d0 key_123 --yes', + '<%= config.bin %> ai service-user revoke-key 66b2f0a1c3d4e5f6a7b8c9d0 key_123 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Service user ID'}), @@ -14,17 +16,14 @@ export default class AiServiceUserRevokeKey extends BaseCommand { } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(AiServiceUserRevokeKey) const client = this.client(flags, {requireProject: false}) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Revoke key ${args.keyId}? It stops working at once.`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Revoke key ${args.keyId}? It stops working at once.`, flags) const res = await client.hub.account.revokeAiServiceUserKey({id: args.id, keyId: args.keyId}) diff --git a/src/commands/ai/service-user/rotate-key.ts b/src/commands/ai/service-user/rotate-key.ts index 3ee33eb..ade6ff4 100644 --- a/src/commands/ai/service-user/rotate-key.ts +++ b/src/commands/ai/service-user/rotate-key.ts @@ -9,8 +9,9 @@ The new key is shown once — store it now. The old keys keep working until you revoke them; --revoke revokes one in the same call.` static examples = [ - '<%= config.bin %> ai service-user rotate-key 66b2f0a1...', - '<%= config.bin %> ai service-user rotate-key 66b2f0a1... --revoke key_123', + '<%= config.bin %> ai service-user rotate-key 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> ai service-user rotate-key 66b2f0a1c3d4e5f6a7b8c9d0 --revoke key_123', + '<%= config.bin %> ai service-user rotate-key 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', ] static args = { @@ -18,6 +19,7 @@ revoke them; --revoke revokes one in the same call.` } static flags = { + ...BaseCommand.dryRunFlags, revoke: Flags.string({description: 'Key ID to revoke at the same time'}), } diff --git a/src/commands/api.ts b/src/commands/api.ts index 4bbcf1b..37d47f1 100644 --- a/src/commands/api.ts +++ b/src/commands/api.ts @@ -1,3 +1,5 @@ +import {Flags} from '@oclif/core' + import {BaseCommand} from '../base.js' import {NamespaceCommand} from '../lib/namespace-command.js' @@ -6,7 +8,7 @@ export default class ApiNs extends NamespaceCommand { static description = `Call any data-plane API endpoint with plain words. -Grammar: norbix api [id] [--field value] +Grammar: norbix api [id] [--field value | --body ''] Same engine as \`norbix hub\` — see \`norbix hub --help\`. For raw HTTP paths use \`norbix raw\`.` @@ -14,6 +16,9 @@ use \`norbix raw\`.` static flags = { ...BaseCommand.mutatingFlags, + body: Flags.string({ + description: 'The whole request as a JSON object (or `-` for stdin); cannot be mixed with --field flags', + }), } static examples = [ @@ -21,6 +26,9 @@ use \`norbix raw\`.` '<%= config.bin %> api database # list database methods', '<%= config.bin %> api database find --collectionName orders --pageSize 20', '<%= config.bin %> api membership users get', + '<%= config.bin %> api membership user delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + '<%= config.bin %> api membership user delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes --json', + `<%= config.bin %> api database insert --body '{"collectionName":"orders","document":"{\\"status\\":\\"new\\"}"}'`, ] async run(): Promise { diff --git a/src/commands/apikeys/regenerate.ts b/src/commands/apikeys/regenerate.ts index 7412c54..c612aed 100644 --- a/src/commands/apikeys/regenerate.ts +++ b/src/commands/apikeys/regenerate.ts @@ -4,7 +4,10 @@ import {BaseCommand} from '../../base.js' export default class ApikeysRegenerate extends BaseCommand { static description = 'Regenerate the API keys for the current environment (old keys stop working!)' - static examples = ['<%= config.bin %> apikeys regenerate --yes'] + static examples = [ + '<%= config.bin %> apikeys regenerate --yes', + '<%= config.bin %> apikeys regenerate --dry-run', + ] static flags = { ...BaseCommand.mutatingFlags, diff --git a/src/commands/config/set.ts b/src/commands/config/set.ts index d28704e..fad486f 100644 --- a/src/commands/config/set.ts +++ b/src/commands/config/set.ts @@ -1,15 +1,16 @@ import {Args} from '@oclif/core' import {BaseCommand} from '../../base.js' -import {SETTABLE_KEYS, isSettableKey, writeStore} from '../../lib/store.js' +import {SETTABLE_KEYS, configFilePath, isSettableKey, writeStore} from '../../lib/store.js' export default class ConfigSet extends BaseCommand { static description = 'Set one configuration value' static examples = [ - '<%= config.bin %> config set projectId 5f1a9f7e...', + '<%= config.bin %> config set projectId 5f1a9f7e2b3c4d5e6f708192', '<%= config.bin %> config set region nb-eu-germany', '<%= config.bin %> config set env TEST', + '<%= config.bin %> config set region nb-eu-germany --dry-run', ] static args = { @@ -17,12 +18,23 @@ export default class ConfigSet extends BaseCommand { value: Args.string({required: true, description: 'New value'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { - const {args} = await this.parse(ConfigSet) + const {args, flags} = await this.parse(ConfigSet) if (!isSettableKey(args.key)) { this.error(`Unknown key "${args.key}". Valid keys: ${SETTABLE_KEYS.join(', ')}`) } + if (flags['dry-run']) { + return this.dryRun({ + method: 'config.set', + request: {file: configFilePath(this.config.configDir), key: args.key, value: args.key === 'apiKey' ? '***' : args.value}, + }) + } + const stored = this.readStore() writeStore(this.config.configDir, {...stored, [args.key]: args.value}) this.print(`${args.key} = ${args.key === 'apiKey' ? '(saved)' : args.value}`) diff --git a/src/commands/config/unset.ts b/src/commands/config/unset.ts index 9fbd627..29de181 100644 --- a/src/commands/config/unset.ts +++ b/src/commands/config/unset.ts @@ -1,23 +1,31 @@ import {Args} from '@oclif/core' import {BaseCommand} from '../../base.js' -import {SETTABLE_KEYS, isSettableKey, writeStore} from '../../lib/store.js' +import {SETTABLE_KEYS, configFilePath, isSettableKey, writeStore} from '../../lib/store.js' export default class ConfigUnset extends BaseCommand { static description = 'Remove one configuration value' - static examples = ['<%= config.bin %> config unset region'] + static examples = ['<%= config.bin %> config unset region', '<%= config.bin %> config unset region --dry-run'] static args = { key: Args.string({required: true, description: `One of: ${SETTABLE_KEYS.join(', ')}`}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { - const {args} = await this.parse(ConfigUnset) + const {args, flags} = await this.parse(ConfigUnset) if (!isSettableKey(args.key)) { this.error(`Unknown key "${args.key}". Valid keys: ${SETTABLE_KEYS.join(', ')}`) } + if (flags['dry-run']) { + return this.dryRun({method: 'config.unset', request: {file: configFilePath(this.config.configDir), key: args.key}}) + } + const stored = this.readStore() writeStore(this.config.configDir, {...stored, [args.key]: undefined}) this.print(`${args.key} removed.`) diff --git a/src/commands/db/aggregate.ts b/src/commands/db/aggregate.ts index 7e5094a..839b45b 100644 --- a/src/commands/db/aggregate.ts +++ b/src/commands/db/aggregate.ts @@ -8,7 +8,7 @@ export default class DbAggregate extends BaseCommand { static examples = [ `<%= config.bin %> db aggregate orders --pipeline '[{"$group":{"_id":"$status","n":{"$sum":1}}}]'`, - '<%= config.bin %> db aggregate orders --id 66b2f0a1...', + '<%= config.bin %> db aggregate orders --id 66b2f0a1c3d4e5f6a7b8c9d0', ] static args = { diff --git a/src/commands/db/aggregates.ts b/src/commands/db/aggregates.ts index f6e821f..f39cfcd 100644 --- a/src/commands/db/aggregates.ts +++ b/src/commands/db/aggregates.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class DbAggregates extends BaseCommand { static description = 'List saved database aggregates' - static examples = ['<%= config.bin %> db aggregates', '<%= config.bin %> db aggregates --schema 66b2...'] + static examples = ['<%= config.bin %> db aggregates', '<%= config.bin %> db aggregates --schema 66b2f0a1c3d4e5f6a7b8c9d0'] static flags = { schema: Flags.string({description: 'Only aggregates of this schema ID'}), diff --git a/src/commands/db/get.ts b/src/commands/db/get.ts index 3ba24f2..4eefb6a 100644 --- a/src/commands/db/get.ts +++ b/src/commands/db/get.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class DbGet extends BaseCommand { static description = 'Get one record by ID' - static examples = ['<%= config.bin %> db get orders 66b2f0a1...'] + static examples = ['<%= config.bin %> db get orders 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { collection: Args.string({required: true, description: 'Collection name'}), diff --git a/src/commands/db/insert-many.ts b/src/commands/db/insert-many.ts index 4d597bb..f883108 100644 --- a/src/commands/db/insert-many.ts +++ b/src/commands/db/insert-many.ts @@ -9,6 +9,7 @@ export default class DbInsertMany extends BaseCommand { static examples = [ `<%= config.bin %> db insert-many orders --docs '[{"a":1},{"a":2}]'`, 'cat orders.json | <%= config.bin %> db insert-many orders --docs -', + `<%= config.bin %> db insert-many orders --docs '[{"a":1},{"a":2}]' --dry-run`, ] static args = { @@ -16,6 +17,7 @@ export default class DbInsertMany extends BaseCommand { } static flags = { + ...BaseCommand.dryRunFlags, docs: Flags.string({char: 'd', required: true, description: 'JSON array of documents (or `-` for stdin)'}), } diff --git a/src/commands/db/insert.ts b/src/commands/db/insert.ts index 5cbbdb4..35d31a0 100644 --- a/src/commands/db/insert.ts +++ b/src/commands/db/insert.ts @@ -9,6 +9,7 @@ export default class DbInsert extends BaseCommand { static examples = [ `<%= config.bin %> db insert orders --doc '{"status":"new","total":9.99}'`, 'cat order.json | <%= config.bin %> db insert orders --doc -', + `<%= config.bin %> db insert orders --doc '{"status":"new","total":9.99}' --dry-run`, ] static args = { @@ -16,6 +17,7 @@ export default class DbInsert extends BaseCommand { } static flags = { + ...BaseCommand.dryRunFlags, doc: Flags.string({char: 'd', required: true, description: 'JSON document (or `-` for stdin)'}), } diff --git a/src/commands/db/replace.ts b/src/commands/db/replace.ts index 131c1b3..eec3c1a 100644 --- a/src/commands/db/replace.ts +++ b/src/commands/db/replace.ts @@ -6,13 +6,17 @@ import {readJsonInput} from '../../lib/json.js' export default class DbReplace extends BaseCommand { static description = 'Replace one record fully (unlike update, the whole document is swapped)' - static examples = [`<%= config.bin %> db replace orders --id 66b2f0a1... --doc '{"status":"new"}'`] + static examples = [ + `<%= config.bin %> db replace orders --id 66b2f0a1c3d4e5f6a7b8c9d0 --doc '{"status":"new"}'`, + `<%= config.bin %> db replace orders --id 66b2f0a1c3d4e5f6a7b8c9d0 --doc '{"status":"new"}' --dry-run`, + ] static args = { collection: Args.string({required: true, description: 'Collection name'}), } static flags = { + ...BaseCommand.dryRunFlags, id: Flags.string({required: true, description: 'Record ID'}), doc: Flags.string({char: 'd', required: true, description: 'JSON replacement document (or `-` for stdin)'}), } diff --git a/src/commands/db/schema.ts b/src/commands/db/schema.ts index 0e6659d..93ea809 100644 --- a/src/commands/db/schema.ts +++ b/src/commands/db/schema.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class DbSchema extends BaseCommand { static description = 'Show one database schema' - static examples = ['<%= config.bin %> db schema 66b2f0a1...'] + static examples = ['<%= config.bin %> db schema 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Schema ID'}), diff --git a/src/commands/db/taxonomy.ts b/src/commands/db/taxonomy.ts index aadc2fd..2fa6008 100644 --- a/src/commands/db/taxonomy.ts +++ b/src/commands/db/taxonomy.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class DbTaxonomy extends BaseCommand { static description = 'Show one database taxonomy' - static examples = ['<%= config.bin %> db taxonomy 66b2f0a1...'] + static examples = ['<%= config.bin %> db taxonomy 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Taxonomy ID'}), diff --git a/src/commands/db/update.ts b/src/commands/db/update.ts index 9e2806c..e254914 100644 --- a/src/commands/db/update.ts +++ b/src/commands/db/update.ts @@ -7,7 +7,7 @@ export default class DbUpdate extends BaseCommand { static description = 'Update records: one by --id, or many by --filter with --many' static examples = [ - `<%= config.bin %> db update orders --id 66b2f0a1... --update '{"$set":{"status":"shipped"}}'`, + `<%= config.bin %> db update orders --id 66b2f0a1c3d4e5f6a7b8c9d0 --update '{"$set":{"status":"shipped"}}'`, `<%= config.bin %> db update orders --filter '{"status":"new"}' --update '{"$set":{"status":"queued"}}' --many --dry-run`, `<%= config.bin %> db update orders --filter '{"status":"new"}' --update '{"$set":{"status":"queued"}}' --many --yes`, ] diff --git a/src/commands/email/archive.ts b/src/commands/email/archive.ts index cd6bdc2..20133ff 100644 --- a/src/commands/email/archive.ts +++ b/src/commands/email/archive.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class EmailArchive extends BaseCommand { static description = 'Archive an email template' - static examples = ['<%= config.bin %> email archive 66b2f0a1...'] + static examples = [ + '<%= config.bin %> email archive 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> email archive 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EmailArchive) const client = this.client(flags) diff --git a/src/commands/email/campaign.ts b/src/commands/email/campaign.ts index 2f17cb0..50e061f 100644 --- a/src/commands/email/campaign.ts +++ b/src/commands/email/campaign.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class EmailCampaign extends BaseCommand { static description = 'Show one email campaign (or its statistics with --stats)' - static examples = ['<%= config.bin %> email campaign 66b2f0a1... --stats'] + static examples = ['<%= config.bin %> email campaign 66b2f0a1c3d4e5f6a7b8c9d0 --stats'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/email/campaign/batch.ts b/src/commands/email/campaign/batch.ts index 1d93111..4e8448a 100644 --- a/src/commands/email/campaign/batch.ts +++ b/src/commands/email/campaign/batch.ts @@ -9,8 +9,8 @@ Give a notification ID as the third argument to see just that e-mail. To see how it looked, run \`email preview --notification \`.` static examples = [ - '<%= config.bin %> email campaign batch 66b2f0a1... b7c3...', - '<%= config.bin %> email campaign batch 66b2f0a1... b7c3... n9d4...', + '<%= config.bin %> email campaign batch 66b2f0a1c3d4e5f6a7b8c9d0 b7c3d2e1f0a9b8c7d6e5f4a3', + '<%= config.bin %> email campaign batch 66b2f0a1c3d4e5f6a7b8c9d0 b7c3d2e1f0a9b8c7d6e5f4a3 9d4e5f6a7b8c9d0e1f2a3b4c', ] static args = { diff --git a/src/commands/email/campaign/batches.ts b/src/commands/email/campaign/batches.ts index b9f5771..0abbd10 100644 --- a/src/commands/email/campaign/batches.ts +++ b/src/commands/email/campaign/batches.ts @@ -6,8 +6,8 @@ export default class EmailCampaignBatches extends BaseCommand { static description = 'List the send batches of an email campaign' static examples = [ - '<%= config.bin %> email campaign batches 66b2f0a1...', - '<%= config.bin %> email campaign batches 66b2f0a1... --email ada@example.com', + '<%= config.bin %> email campaign batches 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> email campaign batches 66b2f0a1c3d4e5f6a7b8c9d0 --email ada@example.com', ] static args = { diff --git a/src/commands/email/campaign/create.ts b/src/commands/email/campaign/create.ts index 0baf32b..c37f589 100644 --- a/src/commands/email/campaign/create.ts +++ b/src/commands/email/campaign/create.ts @@ -19,12 +19,14 @@ every address first. Anything the flags do not cover goes in --config as a JSON object and is merged into the campaign.` static examples = [ - '<%= config.bin %> email campaign create --template 66b2f0a1... --audience all-users --tag beta', - '<%= config.bin %> email campaign create --template 66b2f0a1... --audience emails --email ada@example.com --at 2026-10-01T09:00:00Z', - '<%= config.bin %> email campaign create --template 66b2f0a1... --audience collection --schema subscribers --field email --field-type Email', + '<%= config.bin %> email campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience all-users --tag beta', + '<%= config.bin %> email campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience emails --email ada@example.com --at 2026-10-01T09:00:00Z', + '<%= config.bin %> email campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience collection --schema subscribers --field email --field-type Email', + '<%= config.bin %> email campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience all-users --tag beta --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, template: Flags.string({required: true, description: 'Template ID'}), audience: Flags.string({required: true, description: 'Who receives it', options: Object.keys(EMAIL_AUDIENCES)}), user: Flags.string({description: 'User ID (users / account-users; repeat for several)', multiple: true}), diff --git a/src/commands/email/campaign/delete.ts b/src/commands/email/campaign/delete.ts index 3054d93..d72b2d8 100644 --- a/src/commands/email/campaign/delete.ts +++ b/src/commands/email/campaign/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class EmailCampaignDelete extends BaseCommand { static description = 'Delete an email campaign' - static examples = ['<%= config.bin %> email campaign delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> email campaign delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> email campaign delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Campaign ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(EmailCampaignDelete) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete email campaign ${args.id}?`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete email campaign ${args.id}?`, flags) const res = await client.hub.notifications.deleteEmailCampaign({id: args.id}) diff --git a/src/commands/email/campaign/messages.ts b/src/commands/email/campaign/messages.ts index 9eb6210..0270452 100644 --- a/src/commands/email/campaign/messages.ts +++ b/src/commands/email/campaign/messages.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../../base.js' export default class EmailCampaignMessages extends BaseCommand { static description = 'List the e-mails a campaign sent in one batch' - static examples = ['<%= config.bin %> email campaign messages 66b2f0a1... --batch b7c3...'] + static examples = ['<%= config.bin %> email campaign messages 66b2f0a1c3d4e5f6a7b8c9d0 --batch b7c3d2e1f0a9b8c7d6e5f4a3'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/email/clone.ts b/src/commands/email/clone.ts index 130b2bc..8c819c2 100644 --- a/src/commands/email/clone.ts +++ b/src/commands/email/clone.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class EmailClone extends BaseCommand { static description = 'Clone an email template' - static examples = ['<%= config.bin %> email clone 66b2f0a1...'] + static examples = [ + '<%= config.bin %> email clone 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> email clone 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EmailClone) const client = this.client(flags) diff --git a/src/commands/email/delete.ts b/src/commands/email/delete.ts index a219dad..384ced2 100644 --- a/src/commands/email/delete.ts +++ b/src/commands/email/delete.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class EmailDelete extends BaseCommand { static description = 'Delete an email template' - static examples = ['<%= config.bin %> email delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> email delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> email delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/email/disable.ts b/src/commands/email/disable.ts index 708ea54..d335aea 100644 --- a/src/commands/email/disable.ts +++ b/src/commands/email/disable.ts @@ -1,6 +1,3 @@ -import {confirm} from '@inquirer/prompts' -import {Flags} from '@oclif/core' - import {BaseCommand} from '../../base.js' export default class EmailDisable extends BaseCommand { @@ -8,20 +5,20 @@ export default class EmailDisable extends BaseCommand { Run \`email disable-dependencies\` first to see what still depends on email.` - static examples = ['<%= config.bin %> email disable --yes'] + static examples = [ + '<%= config.bin %> email disable --yes', + '<%= config.bin %> email disable --dry-run', + ] static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {flags} = await this.parse(EmailDisable) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: 'Turn off email for this project?', default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail('Turn off email for this project?', flags) const res = await client.hub.notifications.disableEmail({}) diff --git a/src/commands/email/enable.ts b/src/commands/email/enable.ts index ea0cbdf..e0c0d46 100644 --- a/src/commands/email/enable.ts +++ b/src/commands/email/enable.ts @@ -3,7 +3,14 @@ import {BaseCommand} from '../../base.js' export default class EmailEnable extends BaseCommand { static description = 'Turn on the email module for the project' - static examples = ['<%= config.bin %> email enable'] + static examples = [ + '<%= config.bin %> email enable', + '<%= config.bin %> email enable --dry-run', + ] + + static flags = { + ...BaseCommand.dryRunFlags, + } async run(): Promise { const {flags} = await this.parse(EmailEnable) diff --git a/src/commands/email/footer.ts b/src/commands/email/footer.ts index ec36049..97f2053 100644 --- a/src/commands/email/footer.ts +++ b/src/commands/email/footer.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class EmailFooter extends BaseCommand { static description = 'Show one email footer' - static examples = ['<%= config.bin %> email footer 66b2f0a1...'] + static examples = ['<%= config.bin %> email footer 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Footer ID'}), diff --git a/src/commands/email/footer/delete.ts b/src/commands/email/footer/delete.ts index 5e7cebd..abb00a6 100644 --- a/src/commands/email/footer/delete.ts +++ b/src/commands/email/footer/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class EmailFooterDelete extends BaseCommand { static description = 'Delete an email footer' - static examples = ['<%= config.bin %> email footer delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> email footer delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> email footer delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Footer ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(EmailFooterDelete) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete email footer ${args.id}?`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete email footer ${args.id}?`, flags) const res = await client.hub.notifications.deleteEmailFooter({id: args.id}) diff --git a/src/commands/email/footer/save.ts b/src/commands/email/footer/save.ts index 1e28579..ebdf339 100644 --- a/src/commands/email/footer/save.ts +++ b/src/commands/email/footer/save.ts @@ -10,10 +10,14 @@ to update; the whole footer is sent, so include every language you want to keep. static examples = [ '<%= config.bin %> email footer save --name Default --content-file footer.html', - '<%= config.bin %> email footer save --id 66b2f0a1... --name Default --translations @footer.json', + '<%= config.bin %> email footer save --id 66b2f0a1c3d4e5f6a7b8c9d0 --name Default --translations @footer.json', + '<%= config.bin %> email footer save --name Default --content-file footer.html --dry-run', ] - static flags = snippetFlags('Footer') + static flags = { + ...BaseCommand.dryRunFlags, + ...snippetFlags('Footer'), + } async run(): Promise { const {flags} = await this.parse(EmailFooterSave) diff --git a/src/commands/email/integration.ts b/src/commands/email/integration.ts index 3ada84e..734adee 100644 --- a/src/commands/email/integration.ts +++ b/src/commands/email/integration.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class EmailIntegration extends BaseCommand { static description = 'Show one email integration' - static examples = ['<%= config.bin %> email integration 66b2f0a1...'] + static examples = ['<%= config.bin %> email integration 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Integration ID'}), diff --git a/src/commands/email/integration/confirm-delivery.ts b/src/commands/email/integration/confirm-delivery.ts index d0014c5..b284b57 100644 --- a/src/commands/email/integration/confirm-delivery.ts +++ b/src/commands/email/integration/confirm-delivery.ts @@ -7,12 +7,19 @@ export default class EmailIntegrationConfirmDelivery extends BaseCommand { Run it after \`email integration test\` once you saw the e-mail in the inbox.` - static examples = ['<%= config.bin %> email integration confirm-delivery 66b2f0a1...'] + static examples = [ + '<%= config.bin %> email integration confirm-delivery 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> email integration confirm-delivery 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EmailIntegrationConfirmDelivery) const client = this.client(flags) diff --git a/src/commands/email/integration/default.ts b/src/commands/email/integration/default.ts index 68cd869..83f5027 100644 --- a/src/commands/email/integration/default.ts +++ b/src/commands/email/integration/default.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class EmailIntegrationDefault extends BaseCommand { static description = 'Make an email integration the project default (campaigns use it when they name none)' - static examples = ['<%= config.bin %> email integration default 66b2f0a1...'] + static examples = [ + '<%= config.bin %> email integration default 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> email integration default 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EmailIntegrationDefault) const client = this.client(flags) diff --git a/src/commands/email/integration/delete.ts b/src/commands/email/integration/delete.ts index 63fc6db..969e797 100644 --- a/src/commands/email/integration/delete.ts +++ b/src/commands/email/integration/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class EmailIntegrationDelete extends BaseCommand { static description = 'Delete an email integration' - static examples = ['<%= config.bin %> email integration delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> email integration delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> email integration delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(EmailIntegrationDelete) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete email integration ${args.id}?`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete email integration ${args.id}?`, flags) const res = await client.hub.notifications.deleteEmailIntegration({id: args.id}) diff --git a/src/commands/email/integration/disable.ts b/src/commands/email/integration/disable.ts index 09b77de..ae39fe6 100644 --- a/src/commands/email/integration/disable.ts +++ b/src/commands/email/integration/disable.ts @@ -5,15 +5,23 @@ import {BaseCommand} from '../../../base.js' export default class EmailIntegrationDisable extends BaseCommand { static description = 'Turn an email integration off' - static examples = ['<%= config.bin %> email integration disable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> email integration disable 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> email integration disable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.mutatingFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EmailIntegrationDisable) const client = this.client(flags) + await this.confirmOrFail(`Turn off email integration ${args.id}?`, flags) const res = await client.hub.notifications.disableEmailIntegration({id: args.id}) diff --git a/src/commands/email/integration/domain-health.ts b/src/commands/email/integration/domain-health.ts index 555b66a..14c687b 100644 --- a/src/commands/email/integration/domain-health.ts +++ b/src/commands/email/integration/domain-health.ts @@ -7,7 +7,7 @@ export default class EmailIntegrationDomainHealth extends BaseCommand { Read only — nothing is sent.` - static examples = ['<%= config.bin %> email integration domain-health 66b2f0a1...'] + static examples = ['<%= config.bin %> email integration domain-health 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Integration ID'}), diff --git a/src/commands/email/integration/enable.ts b/src/commands/email/integration/enable.ts index 510ef6f..0b57836 100644 --- a/src/commands/email/integration/enable.ts +++ b/src/commands/email/integration/enable.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class EmailIntegrationEnable extends BaseCommand { static description = 'Turn an email integration on' - static examples = ['<%= config.bin %> email integration enable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> email integration enable 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> email integration enable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EmailIntegrationEnable) const client = this.client(flags) diff --git a/src/commands/email/integration/save.ts b/src/commands/email/integration/save.ts index 07d2bcb..df074c6 100644 --- a/src/commands/email/integration/save.ts +++ b/src/commands/email/integration/save.ts @@ -19,9 +19,11 @@ Pass --id to update an existing integration.` static examples = [ '<%= config.bin %> email integration save --provider Fake', '<%= config.bin %> email integration save --provider SendGrid --name SendGrid --from hello@example.com --config @sendgrid.json', + '<%= config.bin %> email integration save --provider Fake --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, provider: Flags.string({required: true, description: 'Email provider', options: PROVIDERS}), name: Flags.string({description: 'Integration name (Fake ignores it and uses its own)'}), id: Flags.string({description: 'Integration ID — set it to update instead of create'}), diff --git a/src/commands/email/integration/test.ts b/src/commands/email/integration/test.ts index 029fae9..94f5ad2 100644 --- a/src/commands/email/integration/test.ts +++ b/src/commands/email/integration/test.ts @@ -9,9 +9,13 @@ Point this at the Fake integration while you are developing — it accepts the send and contacts no mail service, so nothing reaches a real inbox. With a real provider, run \`email integration confirm-delivery\` once the e-mail arrived.` - static examples = ['<%= config.bin %> email integration test --integration 66b2f0a1... --to dev@example.com'] + static examples = [ + '<%= config.bin %> email integration test --integration 66b2f0a1c3d4e5f6a7b8c9d0 --to dev@example.com', + '<%= config.bin %> email integration test --integration 66b2f0a1c3d4e5f6a7b8c9d0 --to dev@example.com --dry-run', + ] static flags = { + ...BaseCommand.dryRunFlags, integration: Flags.string({required: true, description: 'Integration ID to send through'}), to: Flags.string({required: true, description: 'E-mail address to send the test to'}), } diff --git a/src/commands/email/preview.ts b/src/commands/email/preview.ts index 15833f0..05fb753 100644 --- a/src/commands/email/preview.ts +++ b/src/commands/email/preview.ts @@ -15,9 +15,9 @@ Two ways in: When you are logged in, your session is sent with a hash too.` static examples = [ - '<%= config.bin %> email preview 8f2a91c4...', - '<%= config.bin %> email preview --hash 8f2a91c4... --region nb-eu-germany', - '<%= config.bin %> email preview --notification n9d4...', + '<%= config.bin %> email preview 8f2a91c4d7e6b5a4c3d2e1f0', + '<%= config.bin %> email preview --hash 8f2a91c4d7e6b5a4c3d2e1f0 --region nb-eu-germany', + '<%= config.bin %> email preview --notification 9d4e5f6a7b8c9d0e1f2a3b4c', ] static args = { diff --git a/src/commands/email/signature.ts b/src/commands/email/signature.ts index 5fbdd9e..3ab4837 100644 --- a/src/commands/email/signature.ts +++ b/src/commands/email/signature.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class EmailSignature extends BaseCommand { static description = 'Show one email signature' - static examples = ['<%= config.bin %> email signature 66b2f0a1...'] + static examples = ['<%= config.bin %> email signature 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Signature ID'}), diff --git a/src/commands/email/signature/delete.ts b/src/commands/email/signature/delete.ts index 3ac54d0..bb1cbb3 100644 --- a/src/commands/email/signature/delete.ts +++ b/src/commands/email/signature/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class EmailSignatureDelete extends BaseCommand { static description = 'Delete an email signature' - static examples = ['<%= config.bin %> email signature delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> email signature delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> email signature delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Signature ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(EmailSignatureDelete) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete email signature ${args.id}?`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete email signature ${args.id}?`, flags) const res = await client.hub.notifications.deleteEmailSignature({id: args.id}) diff --git a/src/commands/email/signature/save.ts b/src/commands/email/signature/save.ts index 2896223..d22c4dc 100644 --- a/src/commands/email/signature/save.ts +++ b/src/commands/email/signature/save.ts @@ -10,10 +10,14 @@ to update; the whole signature is sent, so include every language you want to ke static examples = [ '<%= config.bin %> email signature save --name Default --content-file signature.html', - '<%= config.bin %> email signature save --id 66b2f0a1... --name Default --translations @signature.json', + '<%= config.bin %> email signature save --id 66b2f0a1c3d4e5f6a7b8c9d0 --name Default --translations @signature.json', + '<%= config.bin %> email signature save --name Default --content-file signature.html --dry-run', ] - static flags = snippetFlags('Signature') + static flags = { + ...BaseCommand.dryRunFlags, + ...snippetFlags('Signature'), + } async run(): Promise { const {flags} = await this.parse(EmailSignatureSave) diff --git a/src/commands/email/stop.ts b/src/commands/email/stop.ts index 98af91b..9136bfc 100644 --- a/src/commands/email/stop.ts +++ b/src/commands/email/stop.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class EmailStop extends BaseCommand { static description = 'Stop a running email campaign' - static examples = ['<%= config.bin %> email stop 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> email stop 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> email stop 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/email/system-template.ts b/src/commands/email/system-template.ts index e78ca2a..f77a3fc 100644 --- a/src/commands/email/system-template.ts +++ b/src/commands/email/system-template.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class EmailSystemTemplate extends BaseCommand { static description = 'Show one ready-made (system) email template' - static examples = ['<%= config.bin %> email system-template 66b2f0a1...'] + static examples = ['<%= config.bin %> email system-template 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'System template ID'}), diff --git a/src/commands/email/template.ts b/src/commands/email/template.ts index 432d5f4..d17562e 100644 --- a/src/commands/email/template.ts +++ b/src/commands/email/template.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class EmailTemplate extends BaseCommand { static description = 'Show one email template' - static examples = ['<%= config.bin %> email template 66b2f0a1...'] + static examples = ['<%= config.bin %> email template 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/email/template/attach.ts b/src/commands/email/template/attach.ts index e4d87ac..b88fb30 100644 --- a/src/commands/email/template/attach.ts +++ b/src/commands/email/template/attach.ts @@ -13,8 +13,9 @@ JSON, @file.json, or - for stdin). --language attaches it to one translation only.` static examples = [ - '<%= config.bin %> email template attach 66b2f0a1... --file-ref @terms-ref.json', - '<%= config.bin %> email template attach 66b2f0a1... --file-ref @terms-ref.json --language lt', + '<%= config.bin %> email template attach 66b2f0a1c3d4e5f6a7b8c9d0 --file-ref @terms-ref.json', + '<%= config.bin %> email template attach 66b2f0a1c3d4e5f6a7b8c9d0 --file-ref @terms-ref.json --language lt', + '<%= config.bin %> email template attach 66b2f0a1c3d4e5f6a7b8c9d0 --file-ref @terms-ref.json --dry-run', ] static args = { @@ -22,6 +23,7 @@ only.` } static flags = { + ...BaseCommand.dryRunFlags, 'file-ref': Flags.string({required: true, description: 'File reference as a JSON object, @file or -'}), language: Flags.string({description: 'Attach to this translation only'}), } diff --git a/src/commands/email/template/create.ts b/src/commands/email/template/create.ts index 0c4d139..ce88867 100644 --- a/src/commands/email/template/create.ts +++ b/src/commands/email/template/create.ts @@ -14,9 +14,13 @@ Check the code first with \`email template render\`.` static examples = [ '<%= config.bin %> email template create --name Welcome --subject "Hi @Model.Name" --body-file welcome.mjml', '<%= config.bin %> email template create --name Welcome --translations @welcome.json', + '<%= config.bin %> email template create --name Welcome --subject "Hi @Model.Name" --body-file welcome.mjml --dry-run', ] - static flags = emailTemplateFlags + static flags = { + ...BaseCommand.dryRunFlags, + ...emailTemplateFlags, + } async run(): Promise { const {flags} = await this.parse(EmailTemplateCreate) diff --git a/src/commands/email/template/tokens.ts b/src/commands/email/template/tokens.ts index 607f3df..26c94bb 100644 --- a/src/commands/email/template/tokens.ts +++ b/src/commands/email/template/tokens.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../../base.js' export default class EmailTemplateTokens extends BaseCommand { static description = 'List the tokens an email template uses' - static examples = ['<%= config.bin %> email template tokens 66b2f0a1...'] + static examples = ['<%= config.bin %> email template tokens 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/email/template/update.ts b/src/commands/email/template/update.ts index 594daae..e5b09e4 100644 --- a/src/commands/email/template/update.ts +++ b/src/commands/email/template/update.ts @@ -9,14 +9,18 @@ export default class EmailTemplateUpdate extends BaseCommand { The whole template is sent, so pass every language you want to keep.` static examples = [ - '<%= config.bin %> email template update 66b2f0a1... --name Welcome --subject "Hi @Model.Name" --body-file welcome.mjml', + '<%= config.bin %> email template update 66b2f0a1c3d4e5f6a7b8c9d0 --name Welcome --subject "Hi @Model.Name" --body-file welcome.mjml', + '<%= config.bin %> email template update 66b2f0a1c3d4e5f6a7b8c9d0 --name Welcome --subject "Hi @Model.Name" --body-file welcome.mjml --dry-run', ] static args = { id: Args.string({required: true, description: 'Template ID'}), } - static flags = emailTemplateFlags + static flags = { + ...BaseCommand.dryRunFlags, + ...emailTemplateFlags, + } async run(): Promise { const {args, flags} = await this.parse(EmailTemplateUpdate) diff --git a/src/commands/email/unarchive.ts b/src/commands/email/unarchive.ts index 8094b66..f7549fb 100644 --- a/src/commands/email/unarchive.ts +++ b/src/commands/email/unarchive.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class EmailUnarchive extends BaseCommand { static description = 'Restore an archived email template' - static examples = ['<%= config.bin %> email unarchive 66b2f0a1...'] + static examples = [ + '<%= config.bin %> email unarchive 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> email unarchive 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EmailUnarchive) const client = this.client(flags) diff --git a/src/commands/email/validation/save.ts b/src/commands/email/validation/save.ts index cd5a180..0ad3da0 100644 --- a/src/commands/email/validation/save.ts +++ b/src/commands/email/validation/save.ts @@ -15,9 +15,13 @@ credentials (for example {"apiKey": "..."}) in --config — inline JSON, @file.json, or - for stdin — so the key does not land in your shell history. Pass --id to update an existing integration.` - static examples = ['<%= config.bin %> email validation save --provider ZeroBounce --name ZeroBounce --config @zerobounce.json'] + static examples = [ + '<%= config.bin %> email validation save --provider ZeroBounce --name ZeroBounce --config @zerobounce.json', + '<%= config.bin %> email validation save --provider ZeroBounce --name ZeroBounce --config @zerobounce.json --dry-run', + ] static flags = { + ...BaseCommand.dryRunFlags, provider: Flags.string({required: true, description: 'Validation provider', options: PROVIDERS}), name: Flags.string({required: true, description: 'Integration name'}), id: Flags.string({description: 'Integration ID — set it to update instead of create'}), diff --git a/src/commands/email/validation/test.ts b/src/commands/email/validation/test.ts index f4a068f..3c1ad93 100644 --- a/src/commands/email/validation/test.ts +++ b/src/commands/email/validation/test.ts @@ -7,12 +7,19 @@ export default class EmailValidationTest extends BaseCommand { The provider checks one test address; no e-mail is sent.` - static examples = ['<%= config.bin %> email validation test 66b2f0a1...'] + static examples = [ + '<%= config.bin %> email validation test 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> email validation test 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Validation integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EmailValidationTest) const client = this.client(flags) diff --git a/src/commands/env/delete.ts b/src/commands/env/delete.ts index 328ea55..e9e3dfc 100644 --- a/src/commands/env/delete.ts +++ b/src/commands/env/delete.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class EnvDelete extends BaseCommand { static description = 'Delete a non-PROD environment (cascades its integrations)' - static examples = ['<%= config.bin %> env delete STAGING --yes'] + static examples = [ + '<%= config.bin %> env delete STAGING --yes', + '<%= config.bin %> env delete STAGING --dry-run', + ] static args = { name: Args.string({required: true, description: 'Environment name (PROD cannot be deleted)'}), diff --git a/src/commands/env/use.ts b/src/commands/env/use.ts index 1d7bdbb..ef542bf 100644 --- a/src/commands/env/use.ts +++ b/src/commands/env/use.ts @@ -1,8 +1,8 @@ import {Args} from '@oclif/core' import {BaseCommand} from '../../base.js' -import {isSessionValid, readProfiles, readSession, writeProfile, writeSession} from '../../lib/profiles.js' -import {writeStore} from '../../lib/store.js' +import {PROFILES_PATH, SESSION_PATH, isSessionValid, readProfiles, readSession, writeProfile, writeSession} from '../../lib/profiles.js' +import {configFilePath, writeStore} from '../../lib/store.js' export default class EnvUse extends BaseCommand { static description = `Set the default environment for future commands. @@ -15,18 +15,26 @@ profile. Override per command with --env.` '<%= config.bin %> env use TEST', '<%= config.bin %> env use PROD', '<%= config.bin %> env use STAGING --profile fitskin-prod', + '<%= config.bin %> env use TEST --dry-run', ] static args = { name: Args.string({required: true, description: 'Environment name (e.g. PROD, TEST)'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(EnvUse) + const dry = (target: string, file: string) => + this.dryRun({method: 'env.use', request: {env: args.name, target, file}}) if (flags.profile) { const prof = readProfiles()[flags.profile] if (!prof) this.error(`Profile "${flags.profile}" not found. Run \`norbix configure --profile ${flags.profile}\` first.`) + if (flags['dry-run']) return dry(`profile [${flags.profile}]`, PROFILES_PATH) writeProfile(flags.profile, {...prof, env: args.name}) this.print(`Profile [${flags.profile}] now defaults to environment ${args.name}.`) return {env: args.name, profile: flags.profile} @@ -34,6 +42,7 @@ profile. Override per command with --env.` const session = readSession() if (isSessionValid(session)) { + if (flags['dry-run']) return dry('session', SESSION_PATH) writeSession({...session, env: args.name}) this.print(`Session now targets environment ${args.name}.`) return {env: args.name, target: 'session'} @@ -41,12 +50,14 @@ profile. Override per command with --env.` const prof = readProfiles().default if (prof) { + if (flags['dry-run']) return dry('profile [default]', PROFILES_PATH) writeProfile('default', {...prof, env: args.name}) this.print(`Profile [default] now defaults to environment ${args.name}.`) return {env: args.name, profile: 'default'} } // Last resort: legacy config location. + if (flags['dry-run']) return dry('legacy config', configFilePath(this.config.configDir)) writeStore(this.config.configDir, {...this.readStore(), env: args.name}) this.print(`Default environment set to ${args.name}.`) return {env: args.name, target: 'legacy'} diff --git a/src/commands/files/delete.ts b/src/commands/files/delete.ts index 8550690..2bdf884 100644 --- a/src/commands/files/delete.ts +++ b/src/commands/files/delete.ts @@ -6,7 +6,10 @@ import {integrationFlag, resolveIntegration} from '../../lib/files.js' export default class FilesDelete extends BaseCommand { static description = 'Delete one file' - static examples = ['<%= config.bin %> files delete invoices/2026/invoice.pdf --yes'] + static examples = [ + '<%= config.bin %> files delete invoices/2026/invoice.pdf --yes', + '<%= config.bin %> files delete invoices/2026/invoice.pdf --dry-run', + ] static args = { remote: Args.string({required: true, description: 'Remote file path'}), diff --git a/src/commands/files/list.ts b/src/commands/files/list.ts index 78b1168..121a7f7 100644 --- a/src/commands/files/list.ts +++ b/src/commands/files/list.ts @@ -7,7 +7,7 @@ export default class FilesList extends BaseCommand { static description = 'List files in a folder of a files integration' static examples = [ - '<%= config.bin %> files list --integration 66b2...', + '<%= config.bin %> files list --integration 66b2f0a1c3d4e5f6a7b8c9d0', '<%= config.bin %> files list invoices/2026', ] diff --git a/src/commands/files/publish.ts b/src/commands/files/publish.ts index 31de975..fdca457 100644 --- a/src/commands/files/publish.ts +++ b/src/commands/files/publish.ts @@ -2,7 +2,7 @@ import {Args, Flags} from '@oclif/core' import {BaseCommand} from '../../base.js' import {integrationFlag, resolveIntegration} from '../../lib/files.js' -import {callPublicFiles, publicUrlFor} from '../../lib/publicFiles.js' +import {callPublicFiles, publicFilesRequest, publicUrlFor} from '../../lib/publicFiles.js' export default class FilesPublish extends BaseCommand { static args = { @@ -15,9 +15,11 @@ export default class FilesPublish extends BaseCommand { static examples = [ '<%= config.bin %> files publish invoices/2026/invoice.pdf', '<%= config.bin %> files publish invoices --folder', + '<%= config.bin %> files publish invoices/2026/invoice.pdf --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, folder: Flags.boolean({ description: 'Publish the whole folder prefix instead of one file', }), @@ -36,11 +38,13 @@ export default class FilesPublish extends BaseCommand { this.error('No files integration ID. Pass --integration or run `norbix config set filesIntegrationId `.') } - const result = await callPublicFiles( - ctx, - flags.folder ? 'makeFolderPublic' : 'makeFilePublic', - {filesIntegrationId: integration, path: args.remote}, - ) + const operation = flags.folder ? 'makeFolderPublic' : 'makeFilePublic' + const body = {filesIntegrationId: integration, path: args.remote} + if (flags['dry-run']) { + return this.dryRun({method: `hub.files.${operation}`, request: body, http: publicFilesRequest(ctx, operation, body)}) + } + + const result = await callPublicFiles(ctx, operation, body) if (!result.id) { this.error('The gateway did not return a public id.') diff --git a/src/commands/files/unpublish.ts b/src/commands/files/unpublish.ts index 3009466..87442e7 100644 --- a/src/commands/files/unpublish.ts +++ b/src/commands/files/unpublish.ts @@ -2,7 +2,7 @@ import {Args, Flags} from '@oclif/core' import {BaseCommand} from '../../base.js' import {integrationFlag, resolveIntegration} from '../../lib/files.js' -import {callPublicFiles} from '../../lib/publicFiles.js' +import {callPublicFiles, publicFilesRequest} from '../../lib/publicFiles.js' export default class FilesUnpublish extends BaseCommand { static args = { @@ -14,9 +14,11 @@ export default class FilesUnpublish extends BaseCommand { static examples = [ '<%= config.bin %> files unpublish invoices/2026/invoice.pdf', '<%= config.bin %> files unpublish invoices --folder', + '<%= config.bin %> files unpublish invoices/2026/invoice.pdf --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, folder: Flags.boolean({ description: 'Unpublish the whole folder prefix, and every link inside it', }), @@ -33,11 +35,13 @@ export default class FilesUnpublish extends BaseCommand { this.error('No files integration ID. Pass --integration or run `norbix config set filesIntegrationId `.') } - await callPublicFiles( - ctx, - flags.folder ? 'makeFolderPrivate' : 'makeFilePrivate', - {filesIntegrationId: integration, path: args.remote}, - ) + const operation = flags.folder ? 'makeFolderPrivate' : 'makeFilePrivate' + const body = {filesIntegrationId: integration, path: args.remote} + if (flags['dry-run']) { + return this.dryRun({method: `hub.files.${operation}`, request: body, http: publicFilesRequest(ctx, operation, body)}) + } + + await callPublicFiles(ctx, operation, body) this.print( flags.folder diff --git a/src/commands/files/upload.ts b/src/commands/files/upload.ts index 6025d5a..9a8429c 100644 --- a/src/commands/files/upload.ts +++ b/src/commands/files/upload.ts @@ -11,6 +11,7 @@ export default class FilesUpload extends BaseCommand { static examples = [ '<%= config.bin %> files upload ./invoice.pdf', '<%= config.bin %> files upload ./invoice.pdf invoices/2026/invoice.pdf', + '<%= config.bin %> files upload ./invoice.pdf --dry-run', ] static args = { @@ -22,6 +23,7 @@ export default class FilesUpload extends BaseCommand { } static flags = { + ...BaseCommand.dryRunFlags, integration: integrationFlag, 'content-type': Flags.string({description: 'MIME type (default: guessed from extension)'}), } diff --git a/src/commands/hub.ts b/src/commands/hub.ts index 3f12f36..766b3a8 100644 --- a/src/commands/hub.ts +++ b/src/commands/hub.ts @@ -1,3 +1,5 @@ +import {Flags} from '@oclif/core' + import {BaseCommand} from '../base.js' import {NamespaceCommand} from '../lib/namespace-command.js' @@ -6,15 +8,22 @@ export default class Hub extends NamespaceCommand { static description = `Call any Hub endpoint with plain words. -Grammar: norbix hub [id] [--field value] +Grammar: norbix hub [id] [--field value | --body ''] The method is resolved from the words — plural means "list", singular (or a -positional id) means one item. Destructive verbs ask for confirmation -(skip with --yes). Add --dry-run to see what would be called.` +positional id) means one item. Destructive verbs (delete, remove, stop, +disable, block, regenerate, rotate) ask for confirmation; without a terminal +they exit 3 unless --yes. Add --dry-run to see the exact request first. +Field values are typed from the SDK's request type; force one with +--field:str / :num / :bool / :json, or pass the whole request with --body. +\`norbix hub --help\` lists every method with its fields.` static strict = false static flags = { ...BaseCommand.mutatingFlags, + body: Flags.string({ + description: 'The whole request as a JSON object (or `-` for stdin); cannot be mixed with --field flags', + }), } static examples = [ @@ -24,6 +33,10 @@ positional id) means one item. Destructive verbs ask for confirmation '<%= config.bin %> hub database aggregates delete maggr_123 --schemaId sch_456', '<%= config.bin %> hub scheduler tasks get --pageSize 100', '<%= config.bin %> hub logs get --level Error', + '<%= config.bin %> hub scheduler task delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + '<%= config.bin %> hub scheduler task delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes --json', + `<%= config.bin %> hub scheduler task save --body '{"name":"nightly","cronExpression":"0 2 * * *"}'`, + '<%= config.bin %> hub membership user get --userId:str 0042', ] async run(): Promise { diff --git a/src/commands/logout.ts b/src/commands/logout.ts index 143780c..1b50cae 100644 --- a/src/commands/logout.ts +++ b/src/commands/logout.ts @@ -1,6 +1,6 @@ import {BaseCommand} from '../base.js' -import {clearSession} from '../lib/profiles.js' -import {writeStore} from '../lib/store.js' +import {SESSION_PATH, clearSession} from '../lib/profiles.js' +import {configFilePath, writeStore} from '../lib/store.js' export default class Logout extends BaseCommand { static description = `Remove the login session from this machine. @@ -8,10 +8,20 @@ export default class Logout extends BaseCommand { Profiles in ~/.norbix/config (API keys) are NOT touched — manage those with \`norbix configure\` or by editing the file.` - static examples = ['<%= config.bin %> logout'] + static examples = ['<%= config.bin %> logout', '<%= config.bin %> logout --dry-run'] + + static flags = { + ...BaseCommand.dryRunFlags, + } async run(): Promise { - await this.parse(Logout) + const {flags} = await this.parse(Logout) + if (flags['dry-run']) { + return this.dryRun({ + method: 'logout', + request: {removes: SESSION_PATH, clearsTokensIn: configFilePath(this.config.configDir)}, + }) + } clearSession() diff --git a/src/commands/logs/trail.ts b/src/commands/logs/trail.ts index 301cdcf..421a212 100644 --- a/src/commands/logs/trail.ts +++ b/src/commands/logs/trail.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class LogsTrail extends BaseCommand { static description = 'Show the full request trail for one correlation ID' - static examples = ['<%= config.bin %> logs trail 8f2c1b...'] + static examples = ['<%= config.bin %> logs trail 8f2c1b4d-9e3a-4f7b-a1c2-d3e4f5a6b7c8'] static args = { correlationId: Args.string({required: true, description: 'Correlation ID'}), diff --git a/src/commands/module.ts b/src/commands/module.ts index c4c3ca8..fca73de 100644 --- a/src/commands/module.ts +++ b/src/commands/module.ts @@ -22,6 +22,7 @@ export default class Module extends BaseCommand { static examples = [ '<%= config.bin %> module enable database', '<%= config.bin %> module disable sms --yes', + '<%= config.bin %> module enable database --dry-run', ] static args = { diff --git a/src/commands/payments/enable.ts b/src/commands/payments/enable.ts index e9c79c9..b654440 100644 --- a/src/commands/payments/enable.ts +++ b/src/commands/payments/enable.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class PaymentsEnable extends BaseCommand { static description = 'Enable a payment trigger' - static examples = ['<%= config.bin %> payments enable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> payments enable 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> payments enable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Trigger ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(PaymentsEnable) const client = this.client(flags) diff --git a/src/commands/payments/trigger.ts b/src/commands/payments/trigger.ts index 5fa8380..fbe0b9f 100644 --- a/src/commands/payments/trigger.ts +++ b/src/commands/payments/trigger.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class PaymentsTrigger extends BaseCommand { static description = 'Show one payment trigger' - static examples = ['<%= config.bin %> payments trigger 66b2f0a1...'] + static examples = ['<%= config.bin %> payments trigger 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Trigger ID'}), diff --git a/src/commands/project.ts b/src/commands/project.ts index b0e3e5f..49c140a 100644 --- a/src/commands/project.ts +++ b/src/commands/project.ts @@ -8,7 +8,7 @@ export default class Project extends ProjectCommand { Shows the configured project unless you pass another project ID. List every project of the account with \`account projects\`.` - static examples = ['<%= config.bin %> project', '<%= config.bin %> project 66b2f0a1... --json'] + static examples = ['<%= config.bin %> project', '<%= config.bin %> project 66b2f0a1c3d4e5f6a7b8c9d0 --json'] static args = { id: Args.string({description: 'Project ID (defaults to the configured project)'}), diff --git a/src/commands/project/admin-portal/disable.ts b/src/commands/project/admin-portal/disable.ts index 38d8989..0a8851f 100644 --- a/src/commands/project/admin-portal/disable.ts +++ b/src/commands/project/admin-portal/disable.ts @@ -1,21 +1,22 @@ -import {Flags} from '@oclif/core' - import {ProjectCommand} from '../../../lib/project.js' export default class ProjectAdminPortalDisable extends ProjectCommand { static description = "Turn off the project's admin portal (its users can no longer sign in there)" - static examples = ['<%= config.bin %> project admin-portal disable --yes'] + static examples = [ + '<%= config.bin %> project admin-portal disable --yes', + '<%= config.bin %> project admin-portal disable --dry-run', + ] static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...ProjectCommand.mutatingFlags, } async run(): Promise { const {flags} = await this.parse(ProjectAdminPortalDisable) const {client, projectId} = this.projectClient(flags) - if (!(await this.confirmOrStop(flags.yes, 'Turn off the admin portal? Its users can no longer sign in there.'))) return + await this.confirmOrFail('Turn off the admin portal? Its users can no longer sign in there.', flags) const res = await client.hub.account.setAdminPortalEnabled({projectId, enabled: false}) diff --git a/src/commands/project/admin-portal/enable.ts b/src/commands/project/admin-portal/enable.ts index 1c89186..372d21d 100644 --- a/src/commands/project/admin-portal/enable.ts +++ b/src/commands/project/admin-portal/enable.ts @@ -6,7 +6,14 @@ export default class ProjectAdminPortalEnable extends ProjectCommand { The admin portal is the end-user site Norbix generates for the project. See its address with \`project\` (effectiveAdminUrl).` - static examples = ['<%= config.bin %> project admin-portal enable'] + static examples = [ + '<%= config.bin %> project admin-portal enable', + '<%= config.bin %> project admin-portal enable --dry-run', + ] + + static flags = { + ...ProjectCommand.dryRunFlags, + } async run(): Promise { const {flags} = await this.parse(ProjectAdminPortalEnable) diff --git a/src/commands/project/admin-portal/set-service-user.ts b/src/commands/project/admin-portal/set-service-user.ts index d79a8b1..8eac7c9 100644 --- a/src/commands/project/admin-portal/set-service-user.ts +++ b/src/commands/project/admin-portal/set-service-user.ts @@ -7,12 +7,19 @@ export default class ProjectAdminPortalSetServiceUser extends ProjectCommand { The admin portal calls the project's APIs under this user's permissions.` - static examples = ['<%= config.bin %> project admin-portal set-service-user 66b2f0a1...'] + static examples = [ + '<%= config.bin %> project admin-portal set-service-user 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> project admin-portal set-service-user 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Service user ID'}), } + static flags = { + ...ProjectCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(ProjectAdminPortalSetServiceUser) const {client, projectId} = this.projectClient(flags) diff --git a/src/commands/project/admin-portal/set-url.ts b/src/commands/project/admin-portal/set-url.ts index d0545b5..b8bef89 100644 --- a/src/commands/project/admin-portal/set-url.ts +++ b/src/commands/project/admin-portal/set-url.ts @@ -10,6 +10,7 @@ export default class ProjectAdminPortalSetUrl extends ProjectCommand { static examples = [ '<%= config.bin %> project admin-portal set-url https://admin.example.com', '<%= config.bin %> project admin-portal set-url --clear', + '<%= config.bin %> project admin-portal set-url https://admin.example.com --dry-run', ] static args = { @@ -17,6 +18,7 @@ export default class ProjectAdminPortalSetUrl extends ProjectCommand { } static flags = { + ...ProjectCommand.dryRunFlags, clear: Flags.boolean({description: 'Use the Norbix address again', default: false}), } diff --git a/src/commands/project/ai/assistant/create.ts b/src/commands/project/ai/assistant/create.ts index 5e64e00..3253007 100644 --- a/src/commands/project/ai/assistant/create.ts +++ b/src/commands/project/ai/assistant/create.ts @@ -9,9 +9,13 @@ the LLM and model it runs on. List them with \`project ai settings\`.` static examples = [ '<%= config.bin %> project ai assistant create --name Support --system-prompt-file support.md --toolset ai:database-read', + '<%= config.bin %> project ai assistant create --name Support --system-prompt-file support.md --toolset ai:database-read --dry-run', ] - static flags = assistantFlags + static flags = { + ...ProjectCommand.dryRunFlags, + ...assistantFlags, + } async run(): Promise { const {flags} = await this.parse(ProjectAiAssistantCreate) diff --git a/src/commands/project/ai/assistant/delete.ts b/src/commands/project/ai/assistant/delete.ts index fc292dd..838ecd8 100644 --- a/src/commands/project/ai/assistant/delete.ts +++ b/src/commands/project/ai/assistant/delete.ts @@ -1,25 +1,28 @@ -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {ProjectCommand} from '../../../../lib/project.js' export default class ProjectAiAssistantDelete extends ProjectCommand { static description = 'Delete an AI assistant' - static examples = ['<%= config.bin %> project ai assistant delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> project ai assistant delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> project ai assistant delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Assistant ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...ProjectCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(ProjectAiAssistantDelete) const {client, projectId} = this.projectClient(flags) - if (!(await this.confirmOrStop(flags.yes, `Delete AI assistant ${args.id}?`))) return + await this.confirmOrFail(`Delete AI assistant ${args.id}?`, flags) const res = await client.hub.account.deleteProjectAiAssistant({projectId, assistantId: args.id}) diff --git a/src/commands/project/ai/assistant/update.ts b/src/commands/project/ai/assistant/update.ts index 77ccba6..4a839bf 100644 --- a/src/commands/project/ai/assistant/update.ts +++ b/src/commands/project/ai/assistant/update.ts @@ -10,20 +10,24 @@ The server replaces the whole assistant, so this reads it first and changes only what you pass. --no-memory / --no-default turn those off.` static examples = [ - '<%= config.bin %> project ai assistant update 66b2f0a1... --model gpt-4o --no-memory', + '<%= config.bin %> project ai assistant update 66b2f0a1c3d4e5f6a7b8c9d0 --model gpt-4o --no-memory', + '<%= config.bin %> project ai assistant update 66b2f0a1c3d4e5f6a7b8c9d0 --model gpt-4o --no-memory --dry-run', ] static args = { id: Args.string({required: true, description: 'Assistant ID'}), } - static flags = assistantFlags + static flags = { + ...ProjectCommand.dryRunFlags, + ...assistantFlags, + } async run(): Promise { const {args, flags} = await this.parse(ProjectAiAssistantUpdate) - const {client, projectId} = this.projectClient(flags) + const {client, reader, projectId} = this.projectClient(flags) - const settings = (await client.hub.account.getProjectAiSettings({projectId})) as {result?: {assistants?: Assistant[]}} + const settings = (await reader.hub.account.getProjectAiSettings({projectId})) as {result?: {assistants?: Assistant[]}} const current = settings.result?.assistants?.find((a) => a.id === args.id) if (!current) this.error(`No assistant ${args.id} in this project. See them with \`norbix project ai settings\`.`) diff --git a/src/commands/project/ai/settings/set.ts b/src/commands/project/ai/settings/set.ts index addcbd2..21186c6 100644 --- a/src/commands/project/ai/settings/set.ts +++ b/src/commands/project/ai/settings/set.ts @@ -15,11 +15,13 @@ The server replaces all three settings at once, so this reads the current ones first and changes only what you pass.` static examples = [ - '<%= config.bin %> project ai settings set --enable --llm 66b2f0a1... --model gpt-4o-mini', + '<%= config.bin %> project ai settings set --enable --llm 66b2f0a1c3d4e5f6a7b8c9d0 --model gpt-4o-mini', '<%= config.bin %> project ai settings set --disable', + '<%= config.bin %> project ai settings set --enable --llm 66b2f0a1c3d4e5f6a7b8c9d0 --model gpt-4o-mini --dry-run', ] static flags = { + ...ProjectCommand.dryRunFlags, enable: Flags.boolean({description: 'Turn AI chat on', exclusive: ['disable']}), disable: Flags.boolean({description: 'Turn AI chat off', exclusive: ['enable']}), llm: Flags.string({description: 'Default LLM integration ID (see `ai llms`)'}), @@ -32,9 +34,9 @@ first and changes only what you pass.` this.error('Pass --enable, --disable, --llm or --model.') } - const {client, projectId} = this.projectClient(flags) + const {client, reader, projectId} = this.projectClient(flags) - const current = ((await client.hub.account.getProjectAiSettings({projectId})) as {result?: AiSettings}).result ?? {} + const current = ((await reader.hub.account.getProjectAiSettings({projectId})) as {result?: AiSettings}).result ?? {} const res = await client.hub.account.updateProjectAiSettings({ projectId, enabled: flags.enable ? true : flags.disable ? false : (current.enabled ?? false), diff --git a/src/commands/project/cors/add.ts b/src/commands/project/cors/add.ts index 89e8ac3..000f27b 100644 --- a/src/commands/project/cors/add.ts +++ b/src/commands/project/cors/add.ts @@ -8,7 +8,10 @@ export default class ProjectCorsAdd extends ProjectCommand { The server stores the full list, so this reads the current origins first and writes them back with the new ones.` - static examples = ['<%= config.bin %> project cors add https://staging.example.com'] + static examples = [ + '<%= config.bin %> project cors add https://staging.example.com', + '<%= config.bin %> project cors add https://staging.example.com --dry-run', + ] static strict = false @@ -16,12 +19,16 @@ writes them back with the new ones.` origin: Args.string({required: true, description: 'Origins to allow (one or more)'}), } + static flags = { + ...ProjectCommand.dryRunFlags, + } + async run(): Promise { const {argv, flags} = await this.parse(ProjectCorsAdd) const wanted = argv as string[] - const {client, projectId} = this.projectClient(flags) + const {client, reader, projectId} = this.projectClient(flags) - const current = originsOf(await client.hub.account.getProject({projectId})) + const current = originsOf(await reader.hub.account.getProject({projectId})) const added = wanted.filter((o) => !current.some((c) => sameOrigin(c, o))) if (added.length === 0) { this.print('Already allowed — nothing to change.') diff --git a/src/commands/project/cors/remove.ts b/src/commands/project/cors/remove.ts index 85f88b9..4f35f4a 100644 --- a/src/commands/project/cors/remove.ts +++ b/src/commands/project/cors/remove.ts @@ -9,7 +9,10 @@ The server stores the full list, so this reads the current origins first and writes them back without the ones you name. Removing the project's own admin portal origin needs --remove-admin-portal-origin.` - static examples = ['<%= config.bin %> project cors remove https://staging.example.com'] + static examples = [ + '<%= config.bin %> project cors remove https://staging.example.com --yes', + '<%= config.bin %> project cors remove https://staging.example.com --dry-run', + ] static strict = false @@ -18,6 +21,7 @@ portal origin needs --remove-admin-portal-origin.` } static flags = { + ...ProjectCommand.mutatingFlags, 'remove-admin-portal-origin': Flags.boolean({ description: "Allow removing the project's own admin portal origin", default: false, @@ -27,22 +31,25 @@ portal origin needs --remove-admin-portal-origin.` async run(): Promise { const {argv, flags} = await this.parse(ProjectCorsRemove) const unwanted = argv as string[] - const {client, projectId} = this.projectClient(flags) + const {client, reader, projectId} = this.projectClient(flags) - const current = originsOf(await client.hub.account.getProject({projectId})) + const current = originsOf(await reader.hub.account.getProject({projectId})) const origins = current.filter((c) => !unwanted.some((o) => sameOrigin(c, o))) if (origins.length === current.length) { this.print('None of these origins is allowed — nothing to change.') return {origins: current} } + const removed = current.filter((c) => !origins.includes(c)) + await this.confirmOrFail(`Stop allowing ${removed.join(', ')}? Browsers on these origins are refused.`, flags) + await client.hub.account.updateProjectAllowedOrigins({ projectId, origins, removeAdminPortalOrigin: flags['remove-admin-portal-origin'] || undefined, }) - this.print(`Removed: ${current.filter((c) => !origins.includes(c)).join(', ')}.`) + this.print(`Removed: ${removed.join(', ')}.`) return {origins} } } diff --git a/src/commands/project/cors/set.ts b/src/commands/project/cors/set.ts index 39a47c7..29d16fb 100644 --- a/src/commands/project/cors/set.ts +++ b/src/commands/project/cors/set.ts @@ -12,6 +12,7 @@ Every origin not in the list stops being allowed. An origin with no scheme static examples = [ '<%= config.bin %> project cors set https://app.example.com https://example.com', '<%= config.bin %> project cors set https://app.example.com --remove-admin-portal-origin', + '<%= config.bin %> project cors set https://app.example.com https://example.com --dry-run', ] static strict = false @@ -21,6 +22,7 @@ Every origin not in the list stops being allowed. An origin with no scheme } static flags = { + ...ProjectCommand.dryRunFlags, 'remove-admin-portal-origin': Flags.boolean({ description: "Allow the list to drop the project's own admin portal origin", default: false, diff --git a/src/commands/project/delete.ts b/src/commands/project/delete.ts index 67ad74e..97a5a89 100644 --- a/src/commands/project/delete.ts +++ b/src/commands/project/delete.ts @@ -1,5 +1,3 @@ -import {Flags} from '@oclif/core' - import {ProjectCommand} from '../../lib/project.js' export default class ProjectDelete extends ProjectCommand { @@ -8,17 +6,20 @@ export default class ProjectDelete extends ProjectCommand { This removes the project with its data and cannot be undone. Without --yes the command asks first, and refuses when there is no terminal to ask in.` - static examples = ['<%= config.bin %> project delete --project 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> project delete --project 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> project delete --project 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...ProjectCommand.mutatingFlags, } async run(): Promise { const {flags} = await this.parse(ProjectDelete) const {client, projectId} = this.projectClient(flags) - if (!(await this.confirmOrStop(flags.yes, `Delete project ${projectId} and all its data? This cannot be undone.`))) return + await this.confirmOrFail(`Delete project ${projectId} and all its data? This cannot be undone.`, flags) const res = await client.hub.account.deleteProject({projectId}) diff --git a/src/commands/project/disable.ts b/src/commands/project/disable.ts index 1ffd0d5..4223584 100644 --- a/src/commands/project/disable.ts +++ b/src/commands/project/disable.ts @@ -1,5 +1,3 @@ -import {Flags} from '@oclif/core' - import {ProjectCommand} from '../../lib/project.js' export default class ProjectDisable extends ProjectCommand { @@ -7,17 +5,20 @@ export default class ProjectDisable extends ProjectCommand { Its APIs stop answering until \`project enable\`. Nothing is deleted.` - static examples = ['<%= config.bin %> project disable --yes'] + static examples = [ + '<%= config.bin %> project disable --yes', + '<%= config.bin %> project disable --dry-run', + ] static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...ProjectCommand.mutatingFlags, } async run(): Promise { const {flags} = await this.parse(ProjectDisable) const {client, projectId} = this.projectClient(flags) - if (!(await this.confirmOrStop(flags.yes, `Turn off project ${projectId}? Its APIs stop answering.`))) return + await this.confirmOrFail(`Turn off project ${projectId}? Its APIs stop answering.`, flags) const res = await client.hub.account.disableProject({projectId}) diff --git a/src/commands/project/enable.ts b/src/commands/project/enable.ts index c63ec76..04a155d 100644 --- a/src/commands/project/enable.ts +++ b/src/commands/project/enable.ts @@ -3,7 +3,14 @@ import {ProjectCommand} from '../../lib/project.js' export default class ProjectEnable extends ProjectCommand { static description = 'Turn a disabled project back on' - static examples = ['<%= config.bin %> project enable'] + static examples = [ + '<%= config.bin %> project enable', + '<%= config.bin %> project enable --dry-run', + ] + + static flags = { + ...ProjectCommand.dryRunFlags, + } async run(): Promise { const {flags} = await this.parse(ProjectEnable) diff --git a/src/commands/project/legal/expose.ts b/src/commands/project/legal/expose.ts index 5ccc5f3..ddbdb4a 100644 --- a/src/commands/project/legal/expose.ts +++ b/src/commands/project/legal/expose.ts @@ -3,7 +3,14 @@ import {ProjectCommand} from '../../../lib/project.js' export default class ProjectLegalExpose extends ProjectCommand { static description = "Show the project's legal documents to the public (admin portal and the public legal route)" - static examples = ['<%= config.bin %> project legal expose'] + static examples = [ + '<%= config.bin %> project legal expose', + '<%= config.bin %> project legal expose --dry-run', + ] + + static flags = { + ...ProjectCommand.dryRunFlags, + } async run(): Promise { const {flags} = await this.parse(ProjectLegalExpose) diff --git a/src/commands/project/legal/hide.ts b/src/commands/project/legal/hide.ts index 435941a..2c4ef2c 100644 --- a/src/commands/project/legal/hide.ts +++ b/src/commands/project/legal/hide.ts @@ -3,7 +3,14 @@ import {ProjectCommand} from '../../../lib/project.js' export default class ProjectLegalHide extends ProjectCommand { static description = "Stop showing the project's legal documents to the public" - static examples = ['<%= config.bin %> project legal hide'] + static examples = [ + '<%= config.bin %> project legal hide', + '<%= config.bin %> project legal hide --dry-run', + ] + + static flags = { + ...ProjectCommand.dryRunFlags, + } async run(): Promise { const {flags} = await this.parse(ProjectLegalHide) diff --git a/src/commands/project/legal/set.ts b/src/commands/project/legal/set.ts index 745b23b..e8541ee 100644 --- a/src/commands/project/legal/set.ts +++ b/src/commands/project/legal/set.ts @@ -14,9 +14,11 @@ the public with \`project legal expose\`.` '<%= config.bin %> project legal set --terms-file terms.md --privacy-file privacy.md', '<%= config.bin %> project legal set --terms-file terms.md', '<%= config.bin %> project legal set --clear-privacy', + '<%= config.bin %> project legal set --terms-file terms.md --privacy-file privacy.md --dry-run', ] static flags = { + ...ProjectCommand.dryRunFlags, 'terms-file': Flags.string({description: 'Terms & Conditions, a Markdown file', exclusive: ['clear-terms']}), 'privacy-file': Flags.string({description: 'Privacy Policy, a Markdown file', exclusive: ['clear-privacy']}), 'clear-terms': Flags.boolean({description: 'Remove the Terms & Conditions', default: false}), @@ -31,12 +33,12 @@ the public with \`project legal expose\`.` this.error('Pass --terms-file, --privacy-file, --clear-terms or --clear-privacy.') } - const {client, projectId} = this.projectClient(flags) + const {client, reader, projectId} = this.projectClient(flags) let termsMarkdown = terms let privacyMarkdown = privacy if (termsMarkdown === undefined || privacyMarkdown === undefined) { - const current = projectOf(await client.hub.account.getProject({projectId})) + const current = projectOf(await reader.hub.account.getProject({projectId})) termsMarkdown ??= current.legalTermsMarkdown as string | undefined privacyMarkdown ??= current.legalPrivacyMarkdown as string | undefined } diff --git a/src/commands/project/public-config.ts b/src/commands/project/public-config.ts index a663aa5..b783985 100644 --- a/src/commands/project/public-config.ts +++ b/src/commands/project/public-config.ts @@ -8,7 +8,7 @@ export default class ProjectPublicConfig extends ProjectCommand { This is what the admin portal and other apps read before anyone signs in (API host, public route).` - static examples = ['<%= config.bin %> project public-config', '<%= config.bin %> project public-config 66b2f0a1... --json'] + static examples = ['<%= config.bin %> project public-config', '<%= config.bin %> project public-config 66b2f0a1c3d4e5f6a7b8c9d0 --json'] static args = { id: Args.string({description: 'Project ID (defaults to the configured project)'}), diff --git a/src/commands/project/set-colors.ts b/src/commands/project/set-colors.ts index a145e4b..989bae6 100644 --- a/src/commands/project/set-colors.ts +++ b/src/commands/project/set-colors.ts @@ -11,9 +11,11 @@ Colours are hex values such as #1F6FEB.` static examples = [ '<%= config.bin %> project set-colors --main "#1F6FEB" --accent "#F78166"', '<%= config.bin %> project set-colors --accent "#F78166"', + '<%= config.bin %> project set-colors --main "#1F6FEB" --accent "#F78166" --dry-run', ] static flags = { + ...ProjectCommand.dryRunFlags, main: Flags.string({description: 'Main brand colour (hex)'}), accent: Flags.string({description: 'Accent colour (hex)'}), } diff --git a/src/commands/project/set-default-language.ts b/src/commands/project/set-default-language.ts index ed35b2d..6c1ab48 100644 --- a/src/commands/project/set-default-language.ts +++ b/src/commands/project/set-default-language.ts @@ -5,12 +5,19 @@ import {ProjectCommand} from '../../lib/project.js' export default class ProjectSetDefaultLanguage extends ProjectCommand { static description = 'Set the project default language (one of its languages)' - static examples = ['<%= config.bin %> project set-default-language en'] + static examples = [ + '<%= config.bin %> project set-default-language en', + '<%= config.bin %> project set-default-language en --dry-run', + ] static args = { language: Args.string({required: true, description: 'Language code'}), } + static flags = { + ...ProjectCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(ProjectSetDefaultLanguage) const {client, projectId} = this.projectClient(flags) diff --git a/src/commands/project/set-description.ts b/src/commands/project/set-description.ts index 9f9e304..1df474a 100644 --- a/src/commands/project/set-description.ts +++ b/src/commands/project/set-description.ts @@ -8,6 +8,7 @@ export default class ProjectSetDescription extends ProjectCommand { static examples = [ '<%= config.bin %> project set-description "Orders and invoices for the web shop"', '<%= config.bin %> project set-description --clear', + '<%= config.bin %> project set-description "Orders and invoices for the web shop" --dry-run', ] static args = { @@ -15,6 +16,7 @@ export default class ProjectSetDescription extends ProjectCommand { } static flags = { + ...ProjectCommand.dryRunFlags, clear: Flags.boolean({description: 'Remove the description', default: false}), } diff --git a/src/commands/project/set-icon.ts b/src/commands/project/set-icon.ts index 2cb1e41..6dca26f 100644 --- a/src/commands/project/set-icon.ts +++ b/src/commands/project/set-icon.ts @@ -14,9 +14,11 @@ Upload the image first with \`files upload\`, then point at it here.` static examples = [ '<%= config.bin %> project set-icon --file-resource @icon-ref.json', '<%= config.bin %> project set-icon --clear', + '<%= config.bin %> project set-icon --file-resource @icon-ref.json --dry-run', ] static flags = { + ...ProjectCommand.dryRunFlags, 'file-resource': Flags.string({description: 'The stored file as a JSON object, @file or -', exclusive: ['clear']}), clear: Flags.boolean({description: 'Remove the icon', default: false}), } diff --git a/src/commands/project/set-languages.ts b/src/commands/project/set-languages.ts index f32a69c..61b1c0d 100644 --- a/src/commands/project/set-languages.ts +++ b/src/commands/project/set-languages.ts @@ -8,7 +8,10 @@ export default class ProjectSetLanguages extends ProjectCommand { Pass the full list — it replaces the one stored. The default language must stay in it (\`project set-default-language\`).` - static examples = ['<%= config.bin %> project set-languages en lt de'] + static examples = [ + '<%= config.bin %> project set-languages en lt de', + '<%= config.bin %> project set-languages en lt de --dry-run', + ] static strict = false @@ -16,6 +19,10 @@ in it (\`project set-default-language\`).` language: Args.string({required: true, description: 'Language codes (one or more)'}), } + static flags = { + ...ProjectCommand.dryRunFlags, + } + async run(): Promise { const {argv, flags} = await this.parse(ProjectSetLanguages) const languages = argv as string[] diff --git a/src/commands/project/set-logo.ts b/src/commands/project/set-logo.ts index 748ea85..b823e12 100644 --- a/src/commands/project/set-logo.ts +++ b/src/commands/project/set-logo.ts @@ -14,9 +14,11 @@ Upload the image first with \`files upload\`, then point at it here.` static examples = [ '<%= config.bin %> project set-logo --file-resource @logo-ref.json', '<%= config.bin %> project set-logo --clear', + '<%= config.bin %> project set-logo --file-resource @logo-ref.json --dry-run', ] static flags = { + ...ProjectCommand.dryRunFlags, 'file-resource': Flags.string({description: 'The stored file as a JSON object, @file or -', exclusive: ['clear']}), clear: Flags.boolean({description: 'Remove the logo', default: false}), } diff --git a/src/commands/project/set-name.ts b/src/commands/project/set-name.ts index 06f215b..5b2263f 100644 --- a/src/commands/project/set-name.ts +++ b/src/commands/project/set-name.ts @@ -5,12 +5,19 @@ import {ProjectCommand} from '../../lib/project.js' export default class ProjectSetName extends ProjectCommand { static description = 'Rename the project' - static examples = ['<%= config.bin %> project set-name "Shop backend"'] + static examples = [ + '<%= config.bin %> project set-name "Shop backend"', + '<%= config.bin %> project set-name "Shop backend" --dry-run', + ] static args = { name: Args.string({required: true, description: 'New project name'}), } + static flags = { + ...ProjectCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(ProjectSetName) const {client, projectId} = this.projectClient(flags) diff --git a/src/commands/project/set-regions.ts b/src/commands/project/set-regions.ts index 29e79d1..6737602 100644 --- a/src/commands/project/set-regions.ts +++ b/src/commands/project/set-regions.ts @@ -15,9 +15,11 @@ with \`account regions\`.` '<%= config.bin %> project set-regions --additional nb-us-east --additional nb-ap-singapore', '<%= config.bin %> project set-regions --primary nb-eu-germany', '<%= config.bin %> project set-regions --clear-additional', + '<%= config.bin %> project set-regions --additional nb-us-east --additional nb-ap-singapore --dry-run', ] static flags = { + ...ProjectCommand.dryRunFlags, primary: Flags.string({description: 'Primary region code (only when it is not set yet)'}), additional: Flags.string({description: 'Additional region code (repeat for several)', multiple: true, exclusive: ['clear-additional']}), 'clear-additional': Flags.boolean({description: 'Remove every additional region', default: false}), @@ -29,12 +31,12 @@ with \`account regions\`.` this.error('Pass --primary, --additional or --clear-additional.') } - const {client, projectId} = this.projectClient(flags) + const {client, reader, projectId} = this.projectClient(flags) let additionalRegions = flags['clear-additional'] ? [] : flags.additional if (!additionalRegions) { // The list is a full replacement: keep the current one. - const current = projectOf(await client.hub.account.getProject({projectId})) + const current = projectOf(await reader.hub.account.getProject({projectId})) additionalRegions = ((current.additionalRegions as Array<{id: string}> | undefined) ?? []).map((r) => r.id) } diff --git a/src/commands/project/set-url.ts b/src/commands/project/set-url.ts index dee0b21..1f49fa6 100644 --- a/src/commands/project/set-url.ts +++ b/src/commands/project/set-url.ts @@ -11,6 +11,7 @@ in e-mails). The admin portal's own address is \`project admin-portal set-url\`. static examples = [ '<%= config.bin %> project set-url https://shop.example.com', '<%= config.bin %> project set-url --clear', + '<%= config.bin %> project set-url https://shop.example.com --dry-run', ] static args = { @@ -18,6 +19,7 @@ in e-mails). The admin portal's own address is \`project admin-portal set-url\`. } static flags = { + ...ProjectCommand.dryRunFlags, clear: Flags.boolean({description: 'Remove the address', default: false}), } diff --git a/src/commands/project/tokens.ts b/src/commands/project/tokens.ts index 8ec60ed..d71ca09 100644 --- a/src/commands/project/tokens.ts +++ b/src/commands/project/tokens.ts @@ -10,7 +10,7 @@ ids to see the tokens filled in for those users.` static examples = [ '<%= config.bin %> project tokens', - '<%= config.bin %> project tokens --recipient 66b2f0a1...', + '<%= config.bin %> project tokens --recipient 66b2f0a1c3d4e5f6a7b8c9d0', ] static flags = { diff --git a/src/commands/push/archive.ts b/src/commands/push/archive.ts index b524bcc..07ae432 100644 --- a/src/commands/push/archive.ts +++ b/src/commands/push/archive.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class PushArchive extends BaseCommand { static description = 'Archive a push template' - static examples = ['<%= config.bin %> push archive 66b2f0a1...'] + static examples = [ + '<%= config.bin %> push archive 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> push archive 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(PushArchive) const client = this.client(flags) diff --git a/src/commands/push/campaign.ts b/src/commands/push/campaign.ts index 3a6fc5b..14ec3e7 100644 --- a/src/commands/push/campaign.ts +++ b/src/commands/push/campaign.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class PushCampaign extends BaseCommand { static description = 'Show one push campaign (or its statistics with --stats)' - static examples = ['<%= config.bin %> push campaign 66b2f0a1... --stats'] + static examples = ['<%= config.bin %> push campaign 66b2f0a1c3d4e5f6a7b8c9d0 --stats'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/push/campaign/batch.ts b/src/commands/push/campaign/batch.ts index 49b4b56..2c5194a 100644 --- a/src/commands/push/campaign/batch.ts +++ b/src/commands/push/campaign/batch.ts @@ -8,8 +8,8 @@ export default class PushCampaignBatch extends BaseCommand { Give a notification ID as the third argument to see just that notification.` static examples = [ - '<%= config.bin %> push campaign batch 66b2f0a1... b7c3...', - '<%= config.bin %> push campaign batch 66b2f0a1... b7c3... n9d4...', + '<%= config.bin %> push campaign batch 66b2f0a1c3d4e5f6a7b8c9d0 b7c3d4e5f6a7b8c9d0e1f2a3', + '<%= config.bin %> push campaign batch 66b2f0a1c3d4e5f6a7b8c9d0 b7c3d4e5f6a7b8c9d0e1f2a3 n9d4e5f6a7b8c9d0e1f2a3b4', ] static args = { diff --git a/src/commands/push/campaign/batches.ts b/src/commands/push/campaign/batches.ts index b29c9d1..2af70c0 100644 --- a/src/commands/push/campaign/batches.ts +++ b/src/commands/push/campaign/batches.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../../base.js' export default class PushCampaignBatches extends BaseCommand { static description = 'List the send batches of a push campaign' - static examples = ['<%= config.bin %> push campaign batches 66b2f0a1...'] + static examples = ['<%= config.bin %> push campaign batches 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/push/campaign/create.ts b/src/commands/push/campaign/create.ts index 39878b6..3f442e7 100644 --- a/src/commands/push/campaign/create.ts +++ b/src/commands/push/campaign/create.ts @@ -26,12 +26,14 @@ Without --at the campaign is sent right away. Anything the flags do not cover goes in --config as a JSON object and is merged into the campaign.` static examples = [ - '<%= config.bin %> push campaign create --template 66b2f0a1... --audience all-users --tag beta', - '<%= config.bin %> push campaign create --template 66b2f0a1... --audience users --user usr_1 --user usr_2', - '<%= config.bin %> push campaign create --template 66b2f0a1... --audience devices --device dGVzdA==:Ios', + '<%= config.bin %> push campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience all-users --tag beta', + '<%= config.bin %> push campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience users --user usr_1 --user usr_2', + '<%= config.bin %> push campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience devices --device dGVzdA==:Ios', + '<%= config.bin %> push campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience all-users --tag beta --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, template: Flags.string({required: true, description: 'Template ID'}), audience: Flags.string({required: true, description: 'Who receives it', options: Object.keys(AUDIENCES)}), user: Flags.string({description: 'User ID (users / account-users; repeat for several)', multiple: true}), diff --git a/src/commands/push/campaign/delete.ts b/src/commands/push/campaign/delete.ts index 093bb94..0ad3c45 100644 --- a/src/commands/push/campaign/delete.ts +++ b/src/commands/push/campaign/delete.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../../base.js' export default class PushCampaignDelete extends BaseCommand { static description = 'Delete a push campaign' - static examples = ['<%= config.bin %> push campaign delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> push campaign delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> push campaign delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/push/campaign/message.ts b/src/commands/push/campaign/message.ts index fcdbec9..f80e293 100644 --- a/src/commands/push/campaign/message.ts +++ b/src/commands/push/campaign/message.ts @@ -7,7 +7,7 @@ export default class PushCampaignMessage extends BaseCommand { Same as \`push campaign batch \`.` - static examples = ['<%= config.bin %> push campaign message 66b2f0a1... n9d4... --batch b7c3...'] + static examples = ['<%= config.bin %> push campaign message 66b2f0a1c3d4e5f6a7b8c9d0 n9d4e5f6a7b8c9d0e1f2a3b4 --batch b7c3d4e5f6a7b8c9d0e1f2a3'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/push/campaign/messages.ts b/src/commands/push/campaign/messages.ts index 5f6a319..c9a3ec2 100644 --- a/src/commands/push/campaign/messages.ts +++ b/src/commands/push/campaign/messages.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../../base.js' export default class PushCampaignMessages extends BaseCommand { static description = 'List the push messages a campaign sent' - static examples = ['<%= config.bin %> push campaign messages 66b2f0a1... --batch b7c3...'] + static examples = ['<%= config.bin %> push campaign messages 66b2f0a1c3d4e5f6a7b8c9d0 --batch b7c3d4e5f6a7b8c9d0e1f2a3'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/push/clone.ts b/src/commands/push/clone.ts index 95681b3..cd2c17f 100644 --- a/src/commands/push/clone.ts +++ b/src/commands/push/clone.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class PushClone extends BaseCommand { static description = 'Clone a push template' - static examples = ['<%= config.bin %> push clone 66b2f0a1...'] + static examples = [ + '<%= config.bin %> push clone 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> push clone 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(PushClone) const client = this.client(flags) diff --git a/src/commands/push/delete.ts b/src/commands/push/delete.ts index dbc93d9..ceb5958 100644 --- a/src/commands/push/delete.ts +++ b/src/commands/push/delete.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class PushDelete extends BaseCommand { static description = 'Delete a push template' - static examples = ['<%= config.bin %> push delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> push delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> push delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/push/device/register.ts b/src/commands/push/device/register.ts index c833c15..666bfa0 100644 --- a/src/commands/push/device/register.ts +++ b/src/commands/push/device/register.ts @@ -10,9 +10,11 @@ system the token came from; the backend uses it to pick a provider.` static examples = [ '<%= config.bin %> push device register --user usr_123 --token dGVzdA== --os iOS', + '<%= config.bin %> push device register --user usr_123 --token dGVzdA== --os iOS --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, user: Flags.string({required: true, description: 'User ID the device belongs to'}), token: Flags.string({required: true, description: 'Device token from the platform'}), os: Flags.string({required: true, description: 'Operating system, e.g. iOS or Android'}), diff --git a/src/commands/push/disable.ts b/src/commands/push/disable.ts index 69835aa..416b71f 100644 --- a/src/commands/push/disable.ts +++ b/src/commands/push/disable.ts @@ -6,7 +6,10 @@ export default class PushDisable extends BaseCommand { Run \`push disable-dependencies\` first to see what still depends on push.` - static examples = ['<%= config.bin %> push disable --yes'] + static examples = [ + '<%= config.bin %> push disable --yes', + '<%= config.bin %> push disable --dry-run', + ] static flags = { ...BaseCommand.mutatingFlags, diff --git a/src/commands/push/enable.ts b/src/commands/push/enable.ts index ec5be03..a7621fb 100644 --- a/src/commands/push/enable.ts +++ b/src/commands/push/enable.ts @@ -3,7 +3,14 @@ import {BaseCommand} from '../../base.js' export default class PushEnable extends BaseCommand { static description = 'Turn on the push module for the project' - static examples = ['<%= config.bin %> push enable'] + static examples = [ + '<%= config.bin %> push enable', + '<%= config.bin %> push enable --dry-run', + ] + + static flags = { + ...BaseCommand.dryRunFlags, + } async run(): Promise { const {flags} = await this.parse(PushEnable) diff --git a/src/commands/push/integration.ts b/src/commands/push/integration.ts index 22a6065..02dc415 100644 --- a/src/commands/push/integration.ts +++ b/src/commands/push/integration.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class PushIntegration extends BaseCommand { static description = 'Show one push integration' - static examples = ['<%= config.bin %> push integration 66b2f0a1...'] + static examples = ['<%= config.bin %> push integration 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Integration ID'}), diff --git a/src/commands/push/integration/confirm-delivery.ts b/src/commands/push/integration/confirm-delivery.ts index 820d609..33e075e 100644 --- a/src/commands/push/integration/confirm-delivery.ts +++ b/src/commands/push/integration/confirm-delivery.ts @@ -8,12 +8,19 @@ export default class PushIntegrationConfirmDelivery extends BaseCommand { After \`push integration test\`, run this once you have seen the push on the device. It marks the integration as proven to deliver.` - static examples = ['<%= config.bin %> push integration confirm-delivery 66b2f0a1...'] + static examples = [ + '<%= config.bin %> push integration confirm-delivery 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> push integration confirm-delivery 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(PushIntegrationConfirmDelivery) const client = this.client(flags) diff --git a/src/commands/push/integration/default.ts b/src/commands/push/integration/default.ts index c227f52..fa352a0 100644 --- a/src/commands/push/integration/default.ts +++ b/src/commands/push/integration/default.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class PushIntegrationDefault extends BaseCommand { static description = 'Make a push integration the default one for this project' - static examples = ['<%= config.bin %> push integration default 66b2f0a1...'] + static examples = [ + '<%= config.bin %> push integration default 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> push integration default 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(PushIntegrationDefault) const client = this.client(flags) diff --git a/src/commands/push/integration/delete.ts b/src/commands/push/integration/delete.ts index ff8d1dd..7c8b923 100644 --- a/src/commands/push/integration/delete.ts +++ b/src/commands/push/integration/delete.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../../base.js' export default class PushIntegrationDelete extends BaseCommand { static description = 'Delete a push integration' - static examples = ['<%= config.bin %> push integration delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> push integration delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> push integration delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), diff --git a/src/commands/push/integration/enable.ts b/src/commands/push/integration/enable.ts index a043fe2..e9b9192 100644 --- a/src/commands/push/integration/enable.ts +++ b/src/commands/push/integration/enable.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class PushIntegrationEnable extends BaseCommand { static description = 'Turn a push integration on' - static examples = ['<%= config.bin %> push integration enable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> push integration enable 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> push integration enable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(PushIntegrationEnable) const client = this.client(flags) diff --git a/src/commands/push/integration/save.ts b/src/commands/push/integration/save.ts index ef9b84c..29115dd 100644 --- a/src/commands/push/integration/save.ts +++ b/src/commands/push/integration/save.ts @@ -17,9 +17,11 @@ JSON, @file.json, or - for stdin). Pass --id to update an existing integration.` static examples = [ '<%= config.bin %> push integration save --provider Fake', '<%= config.bin %> push integration save --provider AndroidFirebase --name Android --config @firebase.json', + '<%= config.bin %> push integration save --provider Fake --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, provider: Flags.string({required: true, description: 'Push provider', options: PROVIDERS}), name: Flags.string({description: 'Integration name (Fake ignores it and uses its own)'}), id: Flags.string({description: 'Integration ID — set it to update instead of create'}), diff --git a/src/commands/push/integration/test.ts b/src/commands/push/integration/test.ts index 5be776e..4def92b 100644 --- a/src/commands/push/integration/test.ts +++ b/src/commands/push/integration/test.ts @@ -9,10 +9,12 @@ Point this at the Fake integration while you are developing — it accepts the send and contacts no push service, so nothing reaches a real device.` static examples = [ - '<%= config.bin %> push integration test --integration 66b2f0a1... --token dGVzdA== --family Ios', + '<%= config.bin %> push integration test --integration 66b2f0a1c3d4e5f6a7b8c9d0 --token dGVzdA== --family Ios', + '<%= config.bin %> push integration test --integration 66b2f0a1c3d4e5f6a7b8c9d0 --token dGVzdA== --family Ios --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, integration: Flags.string({required: true, description: 'Integration ID to send through'}), token: Flags.string({description: 'Device token to send the test to'}), family: Flags.string({ diff --git a/src/commands/push/preview.ts b/src/commands/push/preview.ts index 8bc65f5..9e4358f 100644 --- a/src/commands/push/preview.ts +++ b/src/commands/push/preview.ts @@ -13,8 +13,8 @@ The signed link is the key: no login and no project are needed. When you are logged in, your session is still sent.` static examples = [ - '<%= config.bin %> push preview 8f2a91c4...', - '<%= config.bin %> push preview --hash 8f2a91c4... --region nb-eu-germany', + '<%= config.bin %> push preview 8f2a91c4d7e6b5a4c3d2e1f0', + '<%= config.bin %> push preview --hash 8f2a91c4d7e6b5a4c3d2e1f0 --region nb-eu-germany', ] static args = { diff --git a/src/commands/push/stop.ts b/src/commands/push/stop.ts index ecbcf82..3fdfe03 100644 --- a/src/commands/push/stop.ts +++ b/src/commands/push/stop.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class PushStop extends BaseCommand { static description = 'Stop a running push campaign' - static examples = ['<%= config.bin %> push stop 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> push stop 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> push stop 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/push/template.ts b/src/commands/push/template.ts index 6f539c0..1f96e4a 100644 --- a/src/commands/push/template.ts +++ b/src/commands/push/template.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class PushTemplate extends BaseCommand { static description = 'Show one push template' - static examples = ['<%= config.bin %> push template 66b2f0a1...'] + static examples = ['<%= config.bin %> push template 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/push/template/create.ts b/src/commands/push/template/create.ts index 0a71901..45c4de5 100644 --- a/src/commands/push/template/create.ts +++ b/src/commands/push/template/create.ts @@ -10,9 +10,13 @@ JSON array of {language, content: {title, body}}.` static examples = [ '<%= config.bin %> push template create --name Welcome --title "Hi @Model.Name" --body "Thanks for joining"', '<%= config.bin %> push template create --name Welcome --translations @welcome.json', + '<%= config.bin %> push template create --name Welcome --title "Hi @Model.Name" --body "Thanks for joining" --dry-run', ] - static flags = templateFlags + static flags = { + ...BaseCommand.dryRunFlags, + ...templateFlags, + } async run(): Promise { const {flags} = await this.parse(PushTemplateCreate) diff --git a/src/commands/push/template/tokens.ts b/src/commands/push/template/tokens.ts index 66ab40b..d448c2e 100644 --- a/src/commands/push/template/tokens.ts +++ b/src/commands/push/template/tokens.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../../base.js' export default class PushTemplateTokens extends BaseCommand { static description = 'List the tokens a push template uses — the values a campaign must fill' - static examples = ['<%= config.bin %> push template tokens 66b2f0a1...'] + static examples = ['<%= config.bin %> push template tokens 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/push/template/update.ts b/src/commands/push/template/update.ts index 1fcd861..4ac188b 100644 --- a/src/commands/push/template/update.ts +++ b/src/commands/push/template/update.ts @@ -9,14 +9,18 @@ export default class PushTemplateUpdate extends BaseCommand { The whole template is sent, so pass every language you want to keep.` static examples = [ - '<%= config.bin %> push template update 66b2f0a1... --name Welcome --title "Hi" --body "Thanks for joining"', + '<%= config.bin %> push template update 66b2f0a1c3d4e5f6a7b8c9d0 --name Welcome --title "Hi" --body "Thanks for joining"', + '<%= config.bin %> push template update 66b2f0a1c3d4e5f6a7b8c9d0 --name Welcome --title "Hi" --body "Thanks for joining" --dry-run', ] static args = { id: Args.string({required: true, description: 'Template ID'}), } - static flags = templateFlags + static flags = { + ...BaseCommand.dryRunFlags, + ...templateFlags, + } async run(): Promise { const {args, flags} = await this.parse(PushTemplateUpdate) diff --git a/src/commands/push/unarchive.ts b/src/commands/push/unarchive.ts index 46f9d55..85c8b6c 100644 --- a/src/commands/push/unarchive.ts +++ b/src/commands/push/unarchive.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class PushUnarchive extends BaseCommand { static description = 'Restore an archived push template' - static examples = ['<%= config.bin %> push unarchive 66b2f0a1...'] + static examples = [ + '<%= config.bin %> push unarchive 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> push unarchive 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(PushUnarchive) const client = this.client(flags) diff --git a/src/commands/raw.ts b/src/commands/raw.ts index ed5c2e7..c7a34e0 100644 --- a/src/commands/raw.ts +++ b/src/commands/raw.ts @@ -19,7 +19,7 @@ path and it becomes v2.` static examples = [ '<%= config.bin %> raw /v2/logs/settings', `<%= config.bin %> raw '/{version}/database/collections/orders/count' --api --query filter='{"status":"paid"}'`, - `<%= config.bin %> raw /v2/scheduler/tasks --method POST --body '{"name":"nightly",...}'`, + `<%= config.bin %> raw /v2/scheduler/tasks --method POST --body '{"name":"nightly","cronExpression":"0 2 * * *"}' --dry-run`, ] static args = { @@ -27,6 +27,7 @@ path and it becomes v2.` } static flags = { + ...BaseCommand.dryRunFlags, api: Flags.boolean({description: 'Call the data-plane API service instead of the hub (hub is the default)', default: false}), method: Flags.string({char: 'X', description: 'HTTP method', default: 'GET', options: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']}), body: Flags.string({char: 'b', description: 'JSON request body (or `-` for stdin)'}), @@ -73,6 +74,14 @@ path and it becomes v2.` headers['Content-Type'] = 'application/json' } + if (flags['dry-run']) { + return this.dryRun({ + method: `raw ${flags.method} ${path}`, + request: body === undefined ? {} : (JSON.parse(body) as unknown), + http: {method: flags.method, url: url.toString(), headers: {...headers, Authorization: 'Bearer ***'}, body: body === undefined ? undefined : (JSON.parse(body) as unknown)}, + }) + } + const res = await fetch(url, {method: flags.method, headers, body}) const text = await res.text() let data: unknown diff --git a/src/commands/scheduler/delete.ts b/src/commands/scheduler/delete.ts index db463a7..546d903 100644 --- a/src/commands/scheduler/delete.ts +++ b/src/commands/scheduler/delete.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class SchedulerDelete extends BaseCommand { static description = 'Delete a scheduler task' - static examples = ['<%= config.bin %> scheduler delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> scheduler delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> scheduler delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Task ID'}), diff --git a/src/commands/scheduler/enable.ts b/src/commands/scheduler/enable.ts index 7597e7c..6759eda 100644 --- a/src/commands/scheduler/enable.ts +++ b/src/commands/scheduler/enable.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class SchedulerEnable extends BaseCommand { static description = 'Enable a scheduler task' - static examples = ['<%= config.bin %> scheduler enable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> scheduler enable 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> scheduler enable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Task ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(SchedulerEnable) const client = this.client(flags) diff --git a/src/commands/scheduler/get.ts b/src/commands/scheduler/get.ts index 0a3f01f..32c0f4a 100644 --- a/src/commands/scheduler/get.ts +++ b/src/commands/scheduler/get.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class SchedulerGet extends BaseCommand { static description = 'Show one scheduler task' - static examples = ['<%= config.bin %> scheduler get 66b2f0a1...'] + static examples = ['<%= config.bin %> scheduler get 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Task ID'}), diff --git a/src/commands/schema.ts b/src/commands/schema.ts new file mode 100644 index 0000000..7bc449a --- /dev/null +++ b/src/commands/schema.ts @@ -0,0 +1,59 @@ +import {Args} from '@oclif/core' + +import {BaseCommand} from '../base.js' +import {usageError} from '../lib/cli-error.js' +import {VALUE_RULES} from '../lib/namespace-command.js' +import {buildSchema, formatSchemaTable, type CommandDescriptor} from '../lib/schema.js' + +export default class Schema extends BaseCommand { + static description = `Describe the CLI as data: every command with its args, flags, examples, whether it is destructive and whether it supports --dry-run. + +Made for coding agents and scripts: run it once with --json, then +\`norbix --help\` for details. The plain-word \`hub\` / \`api\` +commands are dynamic — their methods and request fields come from +\`norbix hub --json\`.` + + static examples = [ + '<%= config.bin %> schema', + '<%= config.bin %> schema --json', + '<%= config.bin %> schema users delete --json', + '<%= config.bin %> schema db --json', + ] + + static strict = false + + static args = { + command: Args.string({required: false, description: 'A command ("users delete") or topic ("db") to describe; omit for everything'}), + } + + async run(): Promise { + const {argv, flags} = await this.parse(Schema) + void flags + const wanted = (argv as string[]).join(' ').trim() + + const own = this.config.commands.filter((c) => c.pluginName === this.config.pjson.name) + const descriptors: CommandDescriptor[] = own.map((c) => ({ + id: c.id, + description: c.description, + hidden: c.hidden, + args: c.args as CommandDescriptor['args'], + flags: c.flags as CommandDescriptor['flags'], + examples: c.examples as CommandDescriptor['examples'], + })) + + const schema = buildSchema({version: this.config.version, bin: this.config.bin, commands: descriptors, valueRules: VALUE_RULES}) + + if (wanted) { + const id = wanted.replaceAll(':', ' ') + const matched = schema.commands.filter((c) => c.id === id || c.id.startsWith(`${id} `)) + if (matched.length === 0) { + throw usageError(`No command or topic "${wanted}".`, 'Run `norbix schema` for the list.', 'norbix schema --help') + } + + schema.commands = matched + } + + this.print(formatSchemaTable(schema)) + return schema + } +} diff --git a/src/commands/sms/archive.ts b/src/commands/sms/archive.ts index dc49c87..e7a264e 100644 --- a/src/commands/sms/archive.ts +++ b/src/commands/sms/archive.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class SmsArchive extends BaseCommand { static description = 'Archive an SMS template' - static examples = ['<%= config.bin %> sms archive 66b2f0a1...'] + static examples = [ + '<%= config.bin %> sms archive 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> sms archive 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(SmsArchive) const client = this.client(flags) diff --git a/src/commands/sms/campaign.ts b/src/commands/sms/campaign.ts index c5f9d59..0d57cb0 100644 --- a/src/commands/sms/campaign.ts +++ b/src/commands/sms/campaign.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class SmsCampaign extends BaseCommand { static description = 'Show one SMS campaign (or its statistics with --stats)' - static examples = ['<%= config.bin %> sms campaign 66b2f0a1... --stats'] + static examples = ['<%= config.bin %> sms campaign 66b2f0a1c3d4e5f6a7b8c9d0 --stats'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/sms/campaign/batch.ts b/src/commands/sms/campaign/batch.ts index 1d581f6..1dc8ad3 100644 --- a/src/commands/sms/campaign/batch.ts +++ b/src/commands/sms/campaign/batch.ts @@ -8,8 +8,8 @@ export default class SmsCampaignBatch extends BaseCommand { Give a notification ID as the third argument to see just that notification.` static examples = [ - '<%= config.bin %> sms campaign batch 66b2f0a1... b7c3...', - '<%= config.bin %> sms campaign batch 66b2f0a1... b7c3... n9d4...', + '<%= config.bin %> sms campaign batch 66b2f0a1c3d4e5f6a7b8c9d0 b7c3d2e1f0a9b8c7d6e5f4a3', + '<%= config.bin %> sms campaign batch 66b2f0a1c3d4e5f6a7b8c9d0 b7c3d2e1f0a9b8c7d6e5f4a3 9d4e5f6a7b8c9d0e1f2a3b4c', ] static args = { diff --git a/src/commands/sms/campaign/batches.ts b/src/commands/sms/campaign/batches.ts index 7d1b3dd..33aa5bf 100644 --- a/src/commands/sms/campaign/batches.ts +++ b/src/commands/sms/campaign/batches.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../../base.js' export default class SmsCampaignBatches extends BaseCommand { static description = 'List the send batches of an SMS campaign' - static examples = ['<%= config.bin %> sms campaign batches 66b2f0a1...'] + static examples = ['<%= config.bin %> sms campaign batches 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/sms/campaign/create.ts b/src/commands/sms/campaign/create.ts index bd4c9d9..b92d628 100644 --- a/src/commands/sms/campaign/create.ts +++ b/src/commands/sms/campaign/create.ts @@ -16,12 +16,14 @@ Without --at the campaign is sent right away. Anything the flags do not cover goes in --config as a JSON object and is merged into the delivery settings.` static examples = [ - '<%= config.bin %> sms campaign create --template 66b2f0a1... --audience all-users --tag beta', - '<%= config.bin %> sms campaign create --template 66b2f0a1... --audience users --user usr_1 --user usr_2', - '<%= config.bin %> sms campaign create --template 66b2f0a1... --audience phone-numbers --phone +37060000000 --at 2026-10-01T09:00:00Z', + '<%= config.bin %> sms campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience all-users --tag beta', + '<%= config.bin %> sms campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience users --user usr_1 --user usr_2', + '<%= config.bin %> sms campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience phone-numbers --phone +37060000000 --at 2026-10-01T09:00:00Z', + '<%= config.bin %> sms campaign create --template 66b2f0a1c3d4e5f6a7b8c9d0 --audience all-users --tag beta --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, template: Flags.string({required: true, description: 'Template ID'}), audience: Flags.string({required: true, description: 'Who receives it', options: Object.keys(SMS_AUDIENCES)}), user: Flags.string({description: 'User ID (users; repeat for several)', multiple: true}), diff --git a/src/commands/sms/campaign/delete.ts b/src/commands/sms/campaign/delete.ts index f3fb30c..b8e1505 100644 --- a/src/commands/sms/campaign/delete.ts +++ b/src/commands/sms/campaign/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class SmsCampaignDelete extends BaseCommand { static description = 'Delete an SMS campaign' - static examples = ['<%= config.bin %> sms campaign delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> sms campaign delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> sms campaign delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Campaign ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(SmsCampaignDelete) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete SMS campaign ${args.id}?`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete SMS campaign ${args.id}?`, flags) const res = await client.hub.notifications.deleteSmsCampaign({id: args.id}) diff --git a/src/commands/sms/campaign/message.ts b/src/commands/sms/campaign/message.ts index ff7a197..a2e5359 100644 --- a/src/commands/sms/campaign/message.ts +++ b/src/commands/sms/campaign/message.ts @@ -7,7 +7,7 @@ export default class SmsCampaignMessage extends BaseCommand { Same as \`sms campaign batch \`.` - static examples = ['<%= config.bin %> sms campaign message 66b2f0a1... n9d4... --batch b7c3...'] + static examples = ['<%= config.bin %> sms campaign message 66b2f0a1c3d4e5f6a7b8c9d0 9d4e5f6a7b8c9d0e1f2a3b4c --batch b7c3d2e1f0a9b8c7d6e5f4a3'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/sms/campaign/messages.ts b/src/commands/sms/campaign/messages.ts index c23394e..725eacd 100644 --- a/src/commands/sms/campaign/messages.ts +++ b/src/commands/sms/campaign/messages.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../../base.js' export default class SmsCampaignMessages extends BaseCommand { static description = 'List the SMS messages a campaign sent' - static examples = ['<%= config.bin %> sms campaign messages 66b2f0a1... --batch b7c3...'] + static examples = ['<%= config.bin %> sms campaign messages 66b2f0a1c3d4e5f6a7b8c9d0 --batch b7c3d2e1f0a9b8c7d6e5f4a3'] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/sms/clone.ts b/src/commands/sms/clone.ts index bbc745a..3bd3477 100644 --- a/src/commands/sms/clone.ts +++ b/src/commands/sms/clone.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class SmsClone extends BaseCommand { static description = 'Clone an SMS template' - static examples = ['<%= config.bin %> sms clone 66b2f0a1...'] + static examples = [ + '<%= config.bin %> sms clone 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> sms clone 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(SmsClone) const client = this.client(flags) diff --git a/src/commands/sms/delete.ts b/src/commands/sms/delete.ts index de2be30..63fc90f 100644 --- a/src/commands/sms/delete.ts +++ b/src/commands/sms/delete.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class SmsDelete extends BaseCommand { static description = 'Delete an SMS template' - static examples = ['<%= config.bin %> sms delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> sms delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> sms delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/sms/disable.ts b/src/commands/sms/disable.ts index f93354e..7b4f186 100644 --- a/src/commands/sms/disable.ts +++ b/src/commands/sms/disable.ts @@ -1,6 +1,3 @@ -import {confirm} from '@inquirer/prompts' -import {Flags} from '@oclif/core' - import {BaseCommand} from '../../base.js' export default class SmsDisable extends BaseCommand { @@ -8,20 +5,20 @@ export default class SmsDisable extends BaseCommand { Run \`sms disable-dependencies\` first to see what still depends on SMS.` - static examples = ['<%= config.bin %> sms disable --yes'] + static examples = [ + '<%= config.bin %> sms disable --yes', + '<%= config.bin %> sms disable --dry-run', + ] static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {flags} = await this.parse(SmsDisable) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: 'Turn off SMS for this project?', default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail('Turn off SMS for this project?', flags) const res = await client.hub.notifications.disableSms({}) diff --git a/src/commands/sms/enable.ts b/src/commands/sms/enable.ts index 1cd47ad..0eb09ae 100644 --- a/src/commands/sms/enable.ts +++ b/src/commands/sms/enable.ts @@ -3,7 +3,14 @@ import {BaseCommand} from '../../base.js' export default class SmsEnable extends BaseCommand { static description = 'Turn on the SMS module for the project' - static examples = ['<%= config.bin %> sms enable'] + static examples = [ + '<%= config.bin %> sms enable', + '<%= config.bin %> sms enable --dry-run', + ] + + static flags = { + ...BaseCommand.dryRunFlags, + } async run(): Promise { const {flags} = await this.parse(SmsEnable) diff --git a/src/commands/sms/integration.ts b/src/commands/sms/integration.ts index eca8d35..3cb1251 100644 --- a/src/commands/sms/integration.ts +++ b/src/commands/sms/integration.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class SmsIntegration extends BaseCommand { static description = 'Show one SMS integration' - static examples = ['<%= config.bin %> sms integration 66b2f0a1...'] + static examples = ['<%= config.bin %> sms integration 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Integration ID'}), diff --git a/src/commands/sms/integration/confirm-delivery.ts b/src/commands/sms/integration/confirm-delivery.ts index 12e51d5..59d4abf 100644 --- a/src/commands/sms/integration/confirm-delivery.ts +++ b/src/commands/sms/integration/confirm-delivery.ts @@ -8,12 +8,19 @@ export default class SmsIntegrationConfirmDelivery extends BaseCommand { After \`sms integration test\`, run this once you have seen the SMS on the phone. It marks the integration as proven to deliver.` - static examples = ['<%= config.bin %> sms integration confirm-delivery 66b2f0a1...'] + static examples = [ + '<%= config.bin %> sms integration confirm-delivery 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> sms integration confirm-delivery 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(SmsIntegrationConfirmDelivery) const client = this.client(flags) diff --git a/src/commands/sms/integration/default.ts b/src/commands/sms/integration/default.ts index b1fa88d..9092151 100644 --- a/src/commands/sms/integration/default.ts +++ b/src/commands/sms/integration/default.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class SmsIntegrationDefault extends BaseCommand { static description = 'Make an SMS integration the default one for this project' - static examples = ['<%= config.bin %> sms integration default 66b2f0a1...'] + static examples = [ + '<%= config.bin %> sms integration default 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> sms integration default 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(SmsIntegrationDefault) const client = this.client(flags) diff --git a/src/commands/sms/integration/delete.ts b/src/commands/sms/integration/delete.ts index 843a052..2343389 100644 --- a/src/commands/sms/integration/delete.ts +++ b/src/commands/sms/integration/delete.ts @@ -1,29 +1,28 @@ -import {confirm} from '@inquirer/prompts' -import {Args, Flags} from '@oclif/core' +import {Args} from '@oclif/core' import {BaseCommand} from '../../../base.js' export default class SmsIntegrationDelete extends BaseCommand { static description = 'Delete an SMS integration' - static examples = ['<%= config.bin %> sms integration delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> sms integration delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> sms integration delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } static flags = { - yes: Flags.boolean({char: 'y', description: 'Skip the confirmation prompt', default: false}), + ...BaseCommand.mutatingFlags, } async run(): Promise { const {args, flags} = await this.parse(SmsIntegrationDelete) const client = this.client(flags) - if (!flags.yes && process.stdout.isTTY) { - const ok = await confirm({message: `Delete SMS integration ${args.id}?`, default: false}) - if (!ok) return this.print('Cancelled.') - } + await this.confirmOrFail(`Delete SMS integration ${args.id}?`, flags) const res = await client.hub.notifications.deleteSmsIntegration({id: args.id}) diff --git a/src/commands/sms/integration/disable.ts b/src/commands/sms/integration/disable.ts index e0a30b5..5881d3d 100644 --- a/src/commands/sms/integration/disable.ts +++ b/src/commands/sms/integration/disable.ts @@ -5,15 +5,23 @@ import {BaseCommand} from '../../../base.js' export default class SmsIntegrationDisable extends BaseCommand { static description = 'Turn an SMS integration off' - static examples = ['<%= config.bin %> sms integration disable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> sms integration disable 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> sms integration disable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.mutatingFlags, + } + async run(): Promise { const {args, flags} = await this.parse(SmsIntegrationDisable) const client = this.client(flags) + await this.confirmOrFail(`Turn off SMS integration ${args.id}?`, flags) // The route token is `{Id}`; the SDK (4.2.0) fills it from `id` — the // lookup is case-insensitive — so the generated field is enough. diff --git a/src/commands/sms/integration/enable.ts b/src/commands/sms/integration/enable.ts index afbbb0a..e76fd7a 100644 --- a/src/commands/sms/integration/enable.ts +++ b/src/commands/sms/integration/enable.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../../base.js' export default class SmsIntegrationEnable extends BaseCommand { static description = 'Turn an SMS integration on' - static examples = ['<%= config.bin %> sms integration enable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> sms integration enable 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> sms integration enable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Integration ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(SmsIntegrationEnable) const client = this.client(flags) diff --git a/src/commands/sms/integration/save.ts b/src/commands/sms/integration/save.ts index bb52211..ccb4f1c 100644 --- a/src/commands/sms/integration/save.ts +++ b/src/commands/sms/integration/save.ts @@ -18,9 +18,11 @@ credentials — put the provider-specific fields in --config (inline JSON, static examples = [ '<%= config.bin %> sms integration save --provider Fake', '<%= config.bin %> sms integration save --provider Twilio --name Twilio --config @twilio.json', + '<%= config.bin %> sms integration save --provider Fake --dry-run', ] static flags = { + ...BaseCommand.dryRunFlags, provider: Flags.string({required: true, description: 'SMS provider', options: PROVIDERS}), name: Flags.string({description: 'Integration name (Fake ignores it and uses its own)'}), id: Flags.string({description: 'Integration ID — set it to update instead of create'}), diff --git a/src/commands/sms/integration/test.ts b/src/commands/sms/integration/test.ts index 483e571..82e33b4 100644 --- a/src/commands/sms/integration/test.ts +++ b/src/commands/sms/integration/test.ts @@ -8,9 +8,13 @@ export default class SmsIntegrationTest extends BaseCommand { Point this at the Fake integration while you are developing — it accepts the send and contacts no SMS service, so nothing reaches a real phone.` - static examples = ['<%= config.bin %> sms integration test --integration 66b2f0a1... --to +37060000000'] + static examples = [ + '<%= config.bin %> sms integration test --integration 66b2f0a1c3d4e5f6a7b8c9d0 --to +37060000000', + '<%= config.bin %> sms integration test --integration 66b2f0a1c3d4e5f6a7b8c9d0 --to +37060000000 --dry-run', + ] static flags = { + ...BaseCommand.dryRunFlags, integration: Flags.string({required: true, description: 'Integration ID to send through'}), to: Flags.string({description: 'Phone number (international format) to send the test to'}), } diff --git a/src/commands/sms/preview.ts b/src/commands/sms/preview.ts index 6537202..068944c 100644 --- a/src/commands/sms/preview.ts +++ b/src/commands/sms/preview.ts @@ -13,8 +13,8 @@ The signed link is the key: no login and no project are needed. When you are logged in, your session is still sent.` static examples = [ - '<%= config.bin %> sms preview 8f2a91c4...', - '<%= config.bin %> sms preview --hash 8f2a91c4... --region nb-eu-germany', + '<%= config.bin %> sms preview 8f2a91c4d7e6b5a4c3d2e1f0', + '<%= config.bin %> sms preview --hash 8f2a91c4d7e6b5a4c3d2e1f0 --region nb-eu-germany', ] static args = { diff --git a/src/commands/sms/stop.ts b/src/commands/sms/stop.ts index b248dce..5a2d55d 100644 --- a/src/commands/sms/stop.ts +++ b/src/commands/sms/stop.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class SmsStop extends BaseCommand { static description = 'Stop a running SMS campaign' - static examples = ['<%= config.bin %> sms stop 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> sms stop 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> sms stop 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Campaign ID'}), diff --git a/src/commands/sms/template.ts b/src/commands/sms/template.ts index 1c74bc9..74b5ade 100644 --- a/src/commands/sms/template.ts +++ b/src/commands/sms/template.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class SmsTemplate extends BaseCommand { static description = 'Show one SMS template' - static examples = ['<%= config.bin %> sms template 66b2f0a1...'] + static examples = ['<%= config.bin %> sms template 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/sms/template/create.ts b/src/commands/sms/template/create.ts index 3674155..b395f36 100644 --- a/src/commands/sms/template/create.ts +++ b/src/commands/sms/template/create.ts @@ -10,9 +10,13 @@ languages: --translations with a JSON array of {language, content: {subject, bod static examples = [ '<%= config.bin %> sms template create --name Welcome --body "Hi @Model.Name, your code is @Model.Code"', '<%= config.bin %> sms template create --name Welcome --translations @welcome.json', + '<%= config.bin %> sms template create --name Welcome --body "Hi @Model.Name, your code is @Model.Code" --dry-run', ] - static flags = smsTemplateFlags + static flags = { + ...BaseCommand.dryRunFlags, + ...smsTemplateFlags, + } async run(): Promise { const {flags} = await this.parse(SmsTemplateCreate) diff --git a/src/commands/sms/template/tokens.ts b/src/commands/sms/template/tokens.ts index ffe52cb..03a74e9 100644 --- a/src/commands/sms/template/tokens.ts +++ b/src/commands/sms/template/tokens.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../../base.js' export default class SmsTemplateTokens extends BaseCommand { static description = 'List the tokens an SMS template uses — the values a campaign must fill' - static examples = ['<%= config.bin %> sms template tokens 66b2f0a1...'] + static examples = ['<%= config.bin %> sms template tokens 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'Template ID'}), diff --git a/src/commands/sms/template/update.ts b/src/commands/sms/template/update.ts index 58259e6..a8993e7 100644 --- a/src/commands/sms/template/update.ts +++ b/src/commands/sms/template/update.ts @@ -9,14 +9,18 @@ export default class SmsTemplateUpdate extends BaseCommand { The whole template is sent, so pass every language you want to keep.` static examples = [ - '<%= config.bin %> sms template update 66b2f0a1... --name Welcome --body "Hi @Model.Name"', + '<%= config.bin %> sms template update 66b2f0a1c3d4e5f6a7b8c9d0 --name Welcome --body "Hi @Model.Name"', + '<%= config.bin %> sms template update 66b2f0a1c3d4e5f6a7b8c9d0 --name Welcome --body "Hi @Model.Name" --dry-run', ] static args = { id: Args.string({required: true, description: 'Template ID'}), } - static flags = smsTemplateFlags + static flags = { + ...BaseCommand.dryRunFlags, + ...smsTemplateFlags, + } async run(): Promise { const {args, flags} = await this.parse(SmsTemplateUpdate) diff --git a/src/commands/sms/unarchive.ts b/src/commands/sms/unarchive.ts index 24aa708..84f0c22 100644 --- a/src/commands/sms/unarchive.ts +++ b/src/commands/sms/unarchive.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class SmsUnarchive extends BaseCommand { static description = 'Restore an archived SMS template' - static examples = ['<%= config.bin %> sms unarchive 66b2f0a1...'] + static examples = [ + '<%= config.bin %> sms unarchive 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> sms unarchive 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'Template ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(SmsUnarchive) const client = this.client(flags) diff --git a/src/commands/users/delete.ts b/src/commands/users/delete.ts index 9e74461..fb9d571 100644 --- a/src/commands/users/delete.ts +++ b/src/commands/users/delete.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class UsersDelete extends BaseCommand { static description = 'Delete a user' - static examples = ['<%= config.bin %> users delete 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> users delete 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> users delete 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'User ID'}), diff --git a/src/commands/users/get.ts b/src/commands/users/get.ts index ee080ea..58662d3 100644 --- a/src/commands/users/get.ts +++ b/src/commands/users/get.ts @@ -5,7 +5,7 @@ import {BaseCommand} from '../../base.js' export default class UsersGet extends BaseCommand { static description = 'Show one user' - static examples = ['<%= config.bin %> users get 66b2f0a1...'] + static examples = ['<%= config.bin %> users get 66b2f0a1c3d4e5f6a7b8c9d0'] static args = { id: Args.string({required: true, description: 'User ID'}), diff --git a/src/commands/users/invite.ts b/src/commands/users/invite.ts index 45fbd56..dafa365 100644 --- a/src/commands/users/invite.ts +++ b/src/commands/users/invite.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class UsersInvite extends BaseCommand { static description = 'Invite a user by email' - static examples = ['<%= config.bin %> users invite alice@example.com'] + static examples = [ + '<%= config.bin %> users invite alice@example.com', + '<%= config.bin %> users invite alice@example.com --dry-run', + ] static args = { email: Args.string({required: true, description: 'Email address to invite'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(UsersInvite) const client = this.client(flags) diff --git a/src/commands/users/unblock.ts b/src/commands/users/unblock.ts index 7942783..df9ea3a 100644 --- a/src/commands/users/unblock.ts +++ b/src/commands/users/unblock.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class UsersUnblock extends BaseCommand { static description = 'Unblock a user' - static examples = ['<%= config.bin %> users unblock 66b2f0a1...'] + static examples = [ + '<%= config.bin %> users unblock 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> users unblock 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { id: Args.string({required: true, description: 'User ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(UsersUnblock) const client = this.client(flags) diff --git a/src/commands/webhooks/enable.ts b/src/commands/webhooks/enable.ts index 2ac2aef..c8be59a 100644 --- a/src/commands/webhooks/enable.ts +++ b/src/commands/webhooks/enable.ts @@ -5,12 +5,19 @@ import {BaseCommand} from '../../base.js' export default class WebhooksEnable extends BaseCommand { static description = 'Enable a webhook destination' - static examples = ['<%= config.bin %> webhooks enable 66b2f0a1...'] + static examples = [ + '<%= config.bin %> webhooks enable 66b2f0a1c3d4e5f6a7b8c9d0', + '<%= config.bin %> webhooks enable 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { destinationId: Args.string({required: true, description: 'Destination ID'}), } + static flags = { + ...BaseCommand.dryRunFlags, + } + async run(): Promise { const {args, flags} = await this.parse(WebhooksEnable) const client = this.client(flags) diff --git a/src/commands/webhooks/remove.ts b/src/commands/webhooks/remove.ts index ffe3168..d731ef7 100644 --- a/src/commands/webhooks/remove.ts +++ b/src/commands/webhooks/remove.ts @@ -5,7 +5,10 @@ import {BaseCommand} from '../../base.js' export default class WebhooksRemove extends BaseCommand { static description = 'Remove a webhook destination' - static examples = ['<%= config.bin %> webhooks remove 66b2f0a1... --yes'] + static examples = [ + '<%= config.bin %> webhooks remove 66b2f0a1c3d4e5f6a7b8c9d0 --yes', + '<%= config.bin %> webhooks remove 66b2f0a1c3d4e5f6a7b8c9d0 --dry-run', + ] static args = { destinationId: Args.string({required: true, description: 'Destination ID'}), diff --git a/src/commands/webhooks/secret.ts b/src/commands/webhooks/secret.ts index bd939b1..037a88b 100644 --- a/src/commands/webhooks/secret.ts +++ b/src/commands/webhooks/secret.ts @@ -8,6 +8,7 @@ export default class WebhooksSecret extends BaseCommand { static examples = [ '<%= config.bin %> webhooks secret', '<%= config.bin %> webhooks secret --rotate --yes', + '<%= config.bin %> webhooks secret --dry-run', ] static flags = { diff --git a/src/generated/request-fields.json b/src/generated/request-fields.json new file mode 100644 index 0000000..61d271f --- /dev/null +++ b/src/generated/request-fields.json @@ -0,0 +1 @@ +{"sdk":"@norbix.ai/ts@2.1.0","api":{"accessToken":{"getAccessToken":{"http":"POST","path":"/access-token","dto":"GetAccessToken","fields":[{"name":"refreshToken","type":"string","kind":"string","required":false}]}},"apikeys":{},"auth":{},"chat":{"askChat":{"http":"POST","path":"/{version}/chat/complete","dto":"AskChatRequest","fields":[{"name":"prompt","type":"string","kind":"string","required":true}]}},"database":{"findMergedTermTree":{"http":"GET","path":"/{version}/database/taxonomies/{taxonomyName}/merged-tree","dto":"FindMergedTermTreeRequest","fields":[{"name":"taxonomyName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"findTaxonomyTree":{"http":"GET","path":"/{version}/database/taxonomies/tree","dto":"FindTaxonomyTreeRequest","fields":[{"name":"includeTerms","type":"boolean","kind":"boolean","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"findTerms":{"http":"GET","path":"/{version}/database/taxonomies/{taxonomyName}/terms","dto":"FindTermsRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"taxonomyName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":false},{"name":"sortDescending","type":"boolean","kind":"boolean","required":true},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"findTermsChildren":{"http":"GET","path":"/{version}/database/taxonomies/{taxonomyName}/terms/{parentId}/children","dto":"FindTermsChildrenRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"taxonomyName","type":"string","kind":"string","required":true},{"name":"parentId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"findTermTree":{"http":"GET","path":"/{version}/database/taxonomies/{taxonomyName}/terms/tree","dto":"FindTermTreeRequest","fields":[{"name":"taxonomyName","type":"string","kind":"string","required":true},{"name":"rootTermId","type":"string","kind":"string","required":false},{"name":"depth","type":"number","kind":"number","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getDatabaseSchema":{"http":"GET","path":"/{version}/database/schemas/{id}","dto":"GetDatabaseSchemaRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseSchemas":{"http":"GET","path":"/{version}/database/schemas","dto":"GetDatabaseSchemasRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"aggregate":{"http":"POST","path":"/{version}/database/collections/{collectionName}/aggregate","dto":"AggregateRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"pipeline","type":"string","kind":"string","required":true}]},"changeResponsibility":{"http":"PUT","path":"/{version}/database/collections/{collectionName}/{id}/responsibility","dto":"ChangeResponsibilityRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"newResponsibleUserId","type":"string","kind":"string","required":true}]},"count":{"http":"GET","path":"/{version}/database/collections/{collectionName}/count","dto":"CountRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":false},{"name":"schemaVersion","type":"number","kind":"number","required":false}]},"deleteMany":{"http":"DELETE","path":"/{version}/database/collections/{collectionName}/many","dto":"DeleteManyRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":true}]},"deleteOne":{"http":"DELETE","path":"/{version}/database/collections/{collectionName}/{id}","dto":"DeleteOneRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"distinct":{"http":"GET","path":"/{version}/database/collections/{collectionName}/distinct","dto":"DistinctRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"field","type":"string","kind":"string","required":true},{"name":"filter","type":"string","kind":"string","required":false},{"name":"schemaVersion","type":"number","kind":"number","required":false}]},"executeAggregate":{"http":"POST","path":"/{version}/database/collections/{collectionName}/aggregates/{aggregateId}/execute","dto":"ExecuteAggregateRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"aggregateId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"find":{"http":"GET","path":"/{version}/database/collections/{collectionName}","dto":"FindRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":false},{"name":"schemaVersion","type":"number","kind":"number","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false},{"name":"sortBy","type":"string","kind":"string","required":false},{"name":"sortOrder","type":"number","kind":"number","required":false}]},"findOne":{"http":"GET","path":"/{version}/database/collections/{collectionName}/{id}","dto":"FindOneRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"findOwn":{"http":"GET","path":"/{version}/database/collections/{collectionName}/own","dto":"FindOwnRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":false},{"name":"schemaVersion","type":"number","kind":"number","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"insertMany":{"http":"POST","path":"/{version}/database/collections/{collectionName}/many","dto":"InsertManyRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"documents","type":"string","kind":"string","required":true}]},"insertOne":{"http":"POST","path":"/{version}/database/collections/{collectionName}","dto":"InsertOneRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"document","type":"string","kind":"string","required":true}]},"replaceOne":{"http":"PUT","path":"/{version}/database/collections/{collectionName}/{id}/replace","dto":"ReplaceOneRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"replacement","type":"string","kind":"string","required":true}]},"updateMany":{"http":"PUT","path":"/{version}/database/collections/{collectionName}/many","dto":"UpdateManyRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":true},{"name":"update","type":"string","kind":"string","required":true}]},"updateOne":{"http":"PUT","path":"/{version}/database/collections/{collectionName}/{id}","dto":"UpdateOneRequest","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"update","type":"string","kind":"string","required":true}]}},"echo":{"echo":{"http":"GET","path":"/{version}/echo","dto":"Echo","fields":[]}},"files":{"commitUpload":{"http":"POST","path":"/{version}/files/{filesIntegrationId}/commit","dto":"CommitUploadRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true},{"name":"contentType","type":"string","kind":"string","required":false},{"name":"sizeBytes","type":"number","kind":"number","required":false},{"name":"fileName","type":"string","kind":"string","required":false}]},"getFileContent":{"http":"GET","path":"/{version}/files/{filesIntegrationId}/content","dto":"GetFileContentRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true},{"name":"token","type":"string","kind":"string","required":false}]},"putFileContent":{"http":"PUT","path":"/{version}/files/{filesIntegrationId}/content","dto":"PutFileContentRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true},{"name":"token","type":"string","kind":"string","required":false}]},"deleteFileApi":{"http":"DELETE","path":"/{version}/files/{filesIntegrationId}","dto":"DeleteFileApiRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true}]},"deleteManyFilesApi":{"http":"DELETE","path":"/{version}/files/{filesIntegrationId}/bulk","dto":"DeleteManyFilesApiRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"paths__","type":"string[]","kind":"string[]","required":true}]},"downloadFileApi":{"http":"GET","path":"/{version}/files/{filesIntegrationId}/download","dto":"DownloadFileApiRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true}]},"getFileInfo":{"http":"GET","path":"/{version}/files/{filesIntegrationId}/info","dto":"GetFileInfoRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true}]},"getSignedUrl":{"http":"GET","path":"/{version}/files/{filesIntegrationId}/sign","dto":"GetSignedUrlRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true},{"name":"expirationSeconds","type":"number","kind":"number","required":false}]},"listFiles":{"http":"GET","path":"/{version}/files/{filesIntegrationId}","dto":"ListFilesRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":false}]},"getPublicFile":{"http":"GET","path":"/{version}/files/public/{PublicId}/{Name*}","dto":"GetPublicFileRequest","fields":[{"name":"publicId","type":"string","kind":"string","required":false},{"name":"name","type":"string","kind":"string","required":false}]},"requestUploadUrl":{"http":"POST","path":"/{version}/files/{filesIntegrationId}/upload-url","dto":"RequestUploadUrlRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true},{"name":"contentType","type":"string","kind":"string","required":true},{"name":"expirationSeconds","type":"number","kind":"number","required":false}]},"testFilesIntegration":{"http":"POST","path":"/{version}/files/{filesIntegrationId}/test","dto":"TestFilesIntegrationRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true}]}},"membership":{"blockUser":{"http":"PATCH","path":"/{version}/membership/auth/block","dto":"BlockUserRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"saveSystemUserWithPermissions":{"http":"POST","path":"/{version}/membership/auth/register/service","dto":"SaveSystemUserWithPermissions","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true},{"name":"roles","type":"string[]","kind":"string[]","required":true}]},"saveGuestUser":{"http":"POST","path":"/{version}/membership/auth/register/guest","dto":"SaveGuestUser","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true}]},"saveUserNameUser":{"http":"POST","path":"/{version}/membership/auth/register/user-name","dto":"SaveUserNameUser","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true},{"name":"password","type":"string","kind":"string","required":true},{"name":"userName","type":"string","kind":"string","required":true}]},"saveEmailUser":{"http":"POST","path":"/{version}/membership/auth/register/email","dto":"SaveEmailUser","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true},{"name":"password","type":"string","kind":"string","required":true},{"name":"email","type":"string","kind":"string","required":true}]},"savePhoneUser":{"http":"POST","path":"/{version}/membership/auth/register/phone","dto":"SavePhoneUser","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true},{"name":"phone","type":"string","kind":"string","required":true}]},"savePhoneUserNameWithPermissions":{"http":"POST","path":"/{version}/membership/auth/register/phone-with-permissions","dto":"SavePhoneUserNameWithPermissions","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true},{"name":"roles","type":"string[]","kind":"string[]","required":true},{"name":"phone","type":"string","kind":"string","required":true}]},"saveEmailUserNameWithPermissions":{"http":"POST","path":"/{version}/membership/auth/register/email-with-permissions","dto":"SaveEmailUserNameWithPermissions","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true},{"name":"roles","type":"string[]","kind":"string[]","required":true},{"name":"password","type":"string","kind":"string","required":true},{"name":"email","type":"string","kind":"string","required":true}]},"saveUserNameWithPermissions":{"http":"POST","path":"/{version}/membership/auth/register/user-name-with-permissions","dto":"SaveUserNameWithPermissions","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true},{"name":"roles","type":"string[]","kind":"string[]","required":true},{"name":"password","type":"string","kind":"string","required":true},{"name":"userName","type":"string","kind":"string","required":true}]},"deleteUser":{"http":"DELETE","path":"/{version}/membership/auth","dto":"DeleteUserRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getUser":{"http":"GET","path":"/{version}/membership/auth/{id}","dto":"GetUserRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getUsers":{"http":"GET","path":"/{version}/membership/auth","dto":"GetUsersRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"includePermissions","type":"boolean","kind":"boolean","required":true},{"name":"userShouldHavePushDevice","type":"boolean","kind":"boolean","required":true},{"name":"userShouldHaveEmail","type":"boolean","kind":"boolean","required":true},{"name":"includeMeta","type":"boolean","kind":"boolean","required":true},{"name":"roleNames","type":"string[]","kind":"string[]","required":false},{"name":"userIds","type":"string[]","kind":"string[]","required":false}]},"getUserPreferences":{"http":"GET","path":"/{version}/membership/auth/{id}/preferences","dto":"GetUserPreferencesRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"grantContactConsent":{"http":"POST","path":"/{version}/membership/users/{contactId}/marketing-state/{channel}/consent","dto":"GrantContactConsentRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"channel","type":"string","kind":"string","required":true},{"name":"lawfulBasis","type":"string","kind":"string","required":true},{"name":"source","type":"string","kind":"string","required":true},{"name":"evidenceRef","type":"string","kind":"string","required":false}]},"inviteUser":{"http":"POST","path":"/{version}/membership/auth/invite","dto":"InviteUserRequest","fields":[{"name":"email","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"linkIdentity":{"http":"POST","path":"/{version}/membership/auth/{userId}/link-identity","dto":"LinkIdentityRequest","fields":[{"name":"userId","type":"string","kind":"string","required":true},{"name":"provider","type":"string","kind":"string","required":true},{"name":"providerToken","type":"string","kind":"string","required":true},{"name":"emailToVerify","type":"string","kind":"string","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"mapAuthToUser":{"http":"POST","path":"/{version}/membership/users/{userId}/map-auth","dto":"MapAuthToUserRequest","fields":[{"name":"userId","type":"string","kind":"string","required":true},{"name":"authId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"assignRolePermissions":{"http":"PUT","path":"/{version}/membership/auth/assign-roles","dto":"AssignRolePermissionsRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"roles","type":"string[]","kind":"string[]","required":false}]},"setContactRoles":{"http":"PUT","path":"/{version}/membership/users/{userId}/roles","dto":"SetContactRolesRequest","fields":[{"name":"userId","type":"string","kind":"string","required":true},{"name":"roles","type":"string[]","kind":"string[]","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"setContactTagSubscription":{"http":"PUT","path":"/{version}/membership/users/{contactId}/marketing-state/{commChannel}/{channel}/tags/{tag}","dto":"SetContactTagSubscriptionRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"commChannel","type":"string","kind":"string","required":true},{"name":"channel","type":"string","kind":"string","required":true},{"name":"tag","type":"string","kind":"string","required":true},{"name":"subscribed","type":"boolean","kind":"boolean","required":true}]},"unblockUser":{"http":"PATCH","path":"/{version}/membership/auth/unblock","dto":"UnblockUserRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"unsubscribeContact":{"http":"POST","path":"/{version}/membership/users/{contactId}/marketing-state/{channel}/unsubscribe","dto":"UnsubscribeContactRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"channel","type":"string","kind":"string","required":true},{"name":"reason","type":"string","kind":"string","required":false}]},"updateUser":{"http":"PUT","path":"/{version}/membership/auth","dto":"UpdateUserRequest","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"userGeneralInfo","type":"UserGeneralInfoDto","kind":"json","required":false},{"name":"userId","type":"string","kind":"string","required":false},{"name":"ignoreUserRegistersAsRole","type":"boolean","kind":"boolean","required":true},{"name":"id","type":"string","kind":"string","required":true}]},"updateUserPreferences":{"http":"PUT","path":"/{version}/membership/auth/{id}/preferences","dto":"UpdateUserPreferencesRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"blockAllMarketingMessages","type":"boolean","kind":"boolean","required":true}]},"changePassword":{"http":"POST","path":"/{version}/membership/userauth/password/change","dto":"ChangePasswordRequest","fields":[{"name":"currentPassword","type":"string","kind":"string","required":true},{"name":"newPassword","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"requestPasswordReset":{"http":"POST","path":"/{version}/membership/userauth/password/reset/request","dto":"RequestPasswordResetRequest","fields":[{"name":"email","type":"string","kind":"string","required":true}]},"confirmPasswordReset":{"http":"POST","path":"/{version}/membership/userauth/password/reset/confirm","dto":"ConfirmPasswordResetRequest","fields":[{"name":"token","type":"string","kind":"string","required":true},{"name":"newPassword","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"passkeyAuthenticationOptions":{"http":"POST","path":"/{version}/membership/userauth/passkey/authentication-options","dto":"PasskeyAuthenticationOptionsRequest","fields":[{"name":"email","type":"string","kind":"string","required":true}]},"verifyPasskeyAuthentication":{"http":"POST","path":"/{version}/membership/userauth/passkey/verify-authentication","dto":"VerifyPasskeyAuthenticationRequest","fields":[{"name":"ceremonyId","type":"string","kind":"string","required":true},{"name":"assertionResponse","type":"string","kind":"string","required":true}]},"listPasskeys":{"http":"GET","path":"/{version}/membership/userauth/passkeys","dto":"ListPasskeysRequest","fields":[]},"renamePasskey":{"http":"POST","path":"/{version}/membership/userauth/passkeys/{CredentialId}/rename","dto":"RenamePasskeyRequest","fields":[{"name":"credentialId","type":"string","kind":"string","required":true},{"name":"friendlyName","type":"string","kind":"string","required":true}]},"revokePasskey":{"http":"POST","path":"/{version}/membership/userauth/passkeys/{CredentialId}/revoke","dto":"RevokePasskeyRequest","fields":[{"name":"credentialId","type":"string","kind":"string","required":true}]},"useRecoveryCode":{"http":"POST","path":"/{version}/membership/userauth/recovery/use-code","dto":"UseRecoveryCodeRequest","fields":[{"name":"email","type":"string","kind":"string","required":true},{"name":"recoveryCode","type":"string","kind":"string","required":true}]},"requestMagicLink":{"http":"POST","path":"/{version}/membership/userauth/recovery/magic-link/request","dto":"RequestMagicLinkRequest","fields":[{"name":"email","type":"string","kind":"string","required":true}]},"consumeMagicLink":{"http":"POST","path":"/{version}/membership/userauth/recovery/magic-link/consume","dto":"ConsumeMagicLinkRequest","fields":[{"name":"token","type":"string","kind":"string","required":true}]},"hasPasskey":{"http":"POST","path":"/{version}/membership/userauth/has-passkey","dto":"HasPasskeyRequest","fields":[{"name":"email","type":"string","kind":"string","required":true}]},"startEmailVerification":{"http":"POST","path":"/{version}/membership/userauth/email/start-verification","dto":"StartEmailVerificationRequest","fields":[{"name":"email","type":"string","kind":"string","required":true}]},"confirmEmailVerification":{"http":"POST","path":"/{version}/membership/userauth/email/confirm-verification","dto":"ConfirmEmailVerificationRequest","fields":[{"name":"email","type":"string","kind":"string","required":true},{"name":"code","type":"string","kind":"string","required":true}]},"passkeyRegistrationOptions":{"http":"POST","path":"/{version}/membership/userauth/passkey/registration-options","dto":"PasskeyRegistrationOptionsRequest","fields":[{"name":"verificationToken","type":"string","kind":"string","required":true}]},"verifyPasskeyRegistration":{"http":"POST","path":"/{version}/membership/userauth/passkey/verify-registration","dto":"VerifyPasskeyRegistrationRequest","fields":[{"name":"verificationToken","type":"string","kind":"string","required":true},{"name":"ceremonyId","type":"string","kind":"string","required":true},{"name":"attestationResponse","type":"string","kind":"string","required":true},{"name":"friendlyName","type":"string","kind":"string","required":false}]},"refreshPasskeyToken":{"http":"POST","path":"/{version}/membership/userauth/token/refresh","dto":"RefreshPasskeyTokenRequest","fields":[{"name":"refreshToken","type":"string","kind":"string","required":false}]},"passkeyLogout":{"http":"POST","path":"/{version}/membership/userauth/logout","dto":"PasskeyLogoutRequest","fields":[{"name":"refreshToken","type":"string","kind":"string","required":false}]}},"public":{"getPublicProjectConfig":{"http":"GET","path":"/{version}/public/projects/{ProjectId}/config","dto":"GetPublicProjectConfig","fields":[]},"getPublicProjectLegal":{"http":"GET","path":"/{version}/public/projects/{ProjectId}/legal/{Kind}","dto":"GetPublicProjectLegal","fields":[{"name":"kind","type":"string","kind":"string","required":false}]}}},"hub":{"accessToken":{"getAccessToken":{"http":"POST","path":"/access-token","dto":"GetAccessToken","fields":[{"name":"refreshToken","type":"string","kind":"string","required":false}]}},"account":{"getAccountProfile":{"http":"GET","path":"/{version}/account/profile","dto":"GetAccountProfile","fields":[]},"updateAccountProfile":{"http":"PUT","path":"/{version}/account/profile","dto":"UpdateAccountProfile","fields":[{"name":"displayName","type":"string","kind":"string","required":true},{"name":"billingEmail","type":"string","kind":"string","required":false},{"name":"operationsEmail","type":"string","kind":"string","required":false},{"name":"securityEmail","type":"string","kind":"string","required":false}]},"resendAccountVerificationToken":{"http":"GET","path":"/{version}/account/verify/resend","dto":"ResendAccountVerificationToken","fields":[]},"getAccountStatus":{"http":"GET","path":"/{version}/account/status","dto":"GetAccountStatus","fields":[]},"createStripeCheckoutSession":{"http":"POST","path":"/{version}/account/stripe/create-checkout-session","dto":"CreateStripeCheckoutSession","fields":[{"name":"subscriptionType","type":"SubscriptionType","kind":"string","required":true,"options":["ManagedService","License"]},{"name":"domain","type":"string","kind":"string","required":false},{"name":"projectCap","type":"number","kind":"number","required":true},{"name":"newProjectSessionId","type":"string","kind":"string","required":false},{"name":"returnUrl","type":"string","kind":"string","required":false}]},"getStripeBillingPortalUrl":{"http":"POST","path":"/{version}/account/stripe/get-portal-url","dto":"GetStripeBillingPortalUrl","fields":[{"name":"subscriptionType","type":"SubscriptionType","kind":"string","required":true,"options":["ManagedService","License"]},{"name":"returnUrl","type":"string","kind":"string","required":false}]},"createTeamMemberFromInvitation":{"http":"POST","path":"/{version}/account/team/member","dto":"CreateTeamMemberFromInvitation","fields":[{"name":"displayName","type":"string","kind":"string","required":true},{"name":"token","type":"string","kind":"string","required":true},{"name":"password","type":"string","kind":"string","required":true}]},"getAccountUsageBilling":{"http":"GET","path":"/{version}/account/usage-billing","dto":"GetAccountUsageBilling","fields":[]},"deleteNotificationsGroup":{"http":"DELETE","path":"/{version}/account/projects/{projectId}/notifications/settings/group","dto":"DeleteNotificationsGroup","fields":[{"name":"groupTag","type":"string","kind":"string","required":true}]},"deleteNotificationsTag":{"http":"DELETE","path":"/{version}/account/projects/{projectId}/notifications/settings/tag","dto":"DeleteNotificationsTag","fields":[{"name":"tag","type":"string","kind":"string","required":true}]},"removeTagFromNotificationsGroup":{"http":"DELETE","path":"/{version}/account/projects/{projectId}/notifications/settings/group/tag","dto":"RemoveTagFromNotificationsGroup","fields":[{"name":"groupTag","type":"string","kind":"string","required":true},{"name":"tag","type":"string","kind":"string","required":true}]},"saveNotificationsGroup":{"http":"POST","path":"/{version}/account/projects/{projectId}/notifications/settings/group","dto":"SaveNotificationsGroup","fields":[{"name":"groupDefinition","type":"GroupDefinitionDto","kind":"json","required":true},{"name":"channel","type":"CommunicationChannel","kind":"string","required":true,"options":["Transactional","Marketing","System"]},{"name":"originChannel","type":"CommunicationChannel","kind":"string","required":false,"options":["Transactional","Marketing","System"]}]},"saveNotificationsTag":{"http":"POST","path":"/{version}/account/projects/{projectId}/notifications/settings/tag","dto":"SaveNotificationsTag","fields":[{"name":"tagDefinition","type":"TagDefinitionDto","kind":"json","required":true},{"name":"channel","type":"CommunicationChannel","kind":"string","required":false,"options":["Transactional","Marketing","System"]},{"name":"groupTag","type":"string","kind":"string","required":false}]},"createProject":{"http":"POST","path":"/{version}/account/projects","dto":"CreateProjectRequest","fields":[{"name":"integration","type":"DatabaseIntegrationRequest","kind":"json","required":true},{"name":"projectName","type":"string","kind":"string","required":true},{"name":"primaryRegion","type":"string","kind":"string","required":false},{"name":"additionalRegions","type":"string[]","kind":"string[]","required":false},{"name":"description","type":"string","kind":"string","required":false}]},"deleteProject":{"http":"DELETE","path":"/{version}/account/projects/{projectId}","dto":"DeleteProject","fields":[]},"createProjectEnvironment":{"http":"POST","path":"/{version}/account/projects/environments","dto":"CreateProjectEnvironmentRequest","fields":[{"name":"environmentName","type":"string","kind":"string","required":true},{"name":"integration","type":"DatabaseIntegrationRequest","kind":"json","required":true}]},"deleteProjectEnvironment":{"http":"DELETE","path":"/{version}/account/projects/environments/{environmentName}","dto":"DeleteProjectEnvironmentRequest","fields":[{"name":"environmentName","type":"string","kind":"string","required":true}]},"setEnvironmentRank":{"http":"PATCH","path":"/{version}/account/projects/environments/{environmentName}/rank","dto":"SetEnvironmentRankRequest","fields":[{"name":"environmentName","type":"string","kind":"string","required":true},{"name":"rank","type":"number","kind":"number","required":true}]},"promoteEnvironment":{"http":"POST","path":"/{version}/account/projects/environments/promote","dto":"PromoteEnvironmentRequest","fields":[{"name":"sourceEnv","type":"string","kind":"string","required":true},{"name":"targetEnv","type":"string","kind":"string","required":true},{"name":"dryRun","type":"boolean","kind":"boolean","required":true}]},"rollbackPromotion":{"http":"POST","path":"/{version}/account/projects/environments/promote/rollback","dto":"RollbackPromotionRequest","fields":[{"name":"targetEnv","type":"string","kind":"string","required":true},{"name":"fromVersion","type":"number","kind":"number","required":true}]},"getProjectEnvironments":{"http":"GET","path":"/{version}/account/projects/environments","dto":"GetProjectEnvironments","fields":[]},"getProject":{"http":"GET","path":"/{version}/account/projects/{projectId}","dto":"GetProject","fields":[]},"getProjects":{"http":"GET","path":"/{version}/account/projects","dto":"GetProjects","fields":[]},"getAccountRegions":{"http":"GET","path":"/{version}/account/regions","dto":"GetAccountRegions","fields":[]},"waitForProjectActive":{"http":"GET","path":"/{version}/account/projects/{projectId}/wait-active","dto":"WaitForProjectActiveRequest","fields":[{"name":"timeoutSeconds","type":"number","kind":"number","required":false}]},"getProjectTokens":{"http":"GET","path":"/{version}/account/projects/{projectId}/tokens","dto":"GetProjectTokens","fields":[{"name":"initiatorId","type":"string","kind":"string","required":false},{"name":"recipientId","type":"string","kind":"string","required":false},{"name":"targetUserId","type":"string","kind":"string","required":false},{"name":"membershipTriggerOldUserId","type":"string","kind":"string","required":false},{"name":"membershipTriggerNewUserId","type":"string","kind":"string","required":false}]},"assignAdminPortalServiceUser":{"http":"PUT","path":"/{version}/account/projects/{projectId}/settings/admin-portal/service-user","dto":"AssignAdminPortalServiceUserRequest","fields":[{"name":"serviceUserId","type":"string","kind":"string","required":true}]},"getAdminPortalStructure":{"http":"GET","path":"/{version}/account/projects/{projectId}/admin-portal/structure","dto":"GetAdminPortalStructure","fields":[]},"updateProjectAdminUrl":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/admin-url","dto":"UpdateProjectAdminUrl","fields":[{"name":"url","type":"string","kind":"string","required":false}]},"updateProjectAccentColor":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/accent-color","dto":"UpdateProjectAccentColor","fields":[{"name":"color","type":"string","kind":"string","required":true}]},"updateProjectIcon":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/icon","dto":"UpdateProjectIcon","fields":[{"name":"fileResource","type":"FileResourceRefDto","kind":"json","required":false}]},"updateProjectLogo":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/logo","dto":"UpdateProjectLogo","fields":[{"name":"fileResource","type":"FileResourceRefDto","kind":"json","required":false}]},"updateProjectMainColor":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/main-color","dto":"UpdateProjectMainColor","fields":[{"name":"color","type":"string","kind":"string","required":true}]},"updateProjectAllowedOrigins":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/origins","dto":"UpdateProjectAllowedOrigins","fields":[{"name":"origins","type":"string[]","kind":"string[]","required":false}]},"updateProjectDefaultLanguage":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/default-language","dto":"UpdateProjectDefaultLanguage","fields":[{"name":"defaultLanguage","type":"string","kind":"string","required":true}]},"updateProjectDescription":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/description","dto":"UpdateProjectDescription","fields":[{"name":"description","type":"string","kind":"string","required":false}]},"disableProject":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/disable","dto":"DisableProject","fields":[]},"enableProject":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/enable","dto":"EnableProject","fields":[]},"updateProjectLanguages":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/languages","dto":"UpdateProjectLanguages","fields":[{"name":"languages","type":"string[]","kind":"string[]","required":true}]},"updateProjectLegalDocuments":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/legal","dto":"UpdateProjectLegalDocuments","fields":[{"name":"termsMarkdown","type":"string","kind":"string","required":false},{"name":"privacyMarkdown","type":"string","kind":"string","required":false}]},"updateProjectExposeLegal":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/legal/expose","dto":"UpdateProjectExposeLegal","fields":[{"name":"exposed","type":"boolean","kind":"boolean","required":true}]},"updateProjectUrl":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/url","dto":"UpdateProjectUrl","fields":[{"name":"url","type":"string","kind":"string","required":false}]},"updateProjectName":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/name","dto":"UpdateProjectName","fields":[{"name":"name","type":"string","kind":"string","required":true}]},"updateProjectRegions":{"http":"PATCH","path":"/{version}/account/projects/{projectId}/settings/regions","dto":"UpdateProjectRegions","fields":[{"name":"primaryRegion","type":"string","kind":"string","required":false},{"name":"additionalRegions","type":"string[]","kind":"string[]","required":false}]},"createAccount":{"http":"POST","path":"/{version}/account","dto":"CreateAccount","fields":[{"name":"displayName","type":"string","kind":"string","required":true},{"name":"email","type":"string","kind":"string","required":true},{"name":"password","type":"string","kind":"string","required":true}]},"changeTeamMemberPassword":{"http":"POST","path":"/{version}/account/team/member/password","dto":"ChangeTeamMemberPassword","fields":[{"name":"email","type":"string","kind":"string","required":true},{"name":"currentPassword","type":"string","kind":"string","required":true},{"name":"newPassword","type":"string","kind":"string","required":true}]},"createTeamMember":{"http":"POST","path":"/{version}/account/team/member/create","dto":"CreateTeamMember","fields":[{"name":"email","type":"string","kind":"string","required":true},{"name":"displayName","type":"string","kind":"string","required":false},{"name":"password","type":"string","kind":"string","required":false},{"name":"roles","type":"string[]","kind":"string[]","required":false},{"name":"sendInvitation","type":"boolean","kind":"boolean","required":true}]},"createAccountPolicy":{"http":"POST","path":"/{version}/account/team/policies","dto":"CreateAccountPolicy","fields":[{"name":"policyName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"policyDocumentJson","type":"string","kind":"string","required":false}]},"createAccountRole":{"http":"POST","path":"/{version}/account/team/roles","dto":"CreateAccountRole","fields":[{"name":"roleName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"policies","type":"string[]","kind":"string[]","required":false}]},"deleteAccountPolicy":{"http":"DELETE","path":"/{version}/account/team/policies/{Id}","dto":"DeleteAccountPolicy","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"deleteAccountRole":{"http":"DELETE","path":"/{version}/account/team/roles/{Id}","dto":"DeleteAccountRole","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getAccountCollaborators":{"http":"GET","path":"/{version}/account/collaborators","dto":"GetAccountCollaborators","fields":[{"name":"includeAccountOwner","type":"boolean","kind":"boolean","required":true},{"name":"userShouldHavePushDevice","type":"boolean","kind":"boolean","required":true},{"name":"userIds","type":"string[]","kind":"string[]","required":false},{"name":"roleNames","type":"string[]","kind":"string[]","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"getAccountPasswordPolicy":{"http":"GET","path":"/{version}/account/team/password-policy","dto":"GetAccountPasswordPolicy","fields":[]},"getAccountTeamPolicies":{"http":"GET","path":"/{version}/account/team/policies","dto":"GetAccountTeamPolicies","fields":[]},"getAccountTeamRoles":{"http":"GET","path":"/{version}/account/team/roles","dto":"GetAccountTeamRoles","fields":[]},"sendInviteToTeamMember":{"http":"POST","path":"/{version}/account/team/member/invite","dto":"SendInviteToTeamMember","fields":[{"name":"email","type":"string","kind":"string","required":true},{"name":"roles","type":"string[]","kind":"string[]","required":false}]},"updateAccountPolicy":{"http":"PUT","path":"/{version}/account/team/policies","dto":"UpdateAccountPolicy","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"policyName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"policyDocumentJson","type":"string","kind":"string","required":false}]},"updateAccountRole":{"http":"PUT","path":"/{version}/account/team/roles","dto":"UpdateAccountRole","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"roleName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"policies","type":"string[]","kind":"string[]","required":false}]},"accountHasPasskey":{"http":"POST","path":"/{version}/account/userauth/has-passkey","dto":"AccountHasPasskeyRequest","fields":[{"name":"email","type":"string","kind":"string","required":true}]},"accountStartEmailVerification":{"http":"POST","path":"/{version}/account/userauth/email/start-verification","dto":"AccountStartEmailVerificationRequest","fields":[{"name":"email","type":"string","kind":"string","required":true}]},"accountConfirmEmailVerification":{"http":"POST","path":"/{version}/account/userauth/email/confirm-verification","dto":"AccountConfirmEmailVerificationRequest","fields":[{"name":"email","type":"string","kind":"string","required":true},{"name":"code","type":"string","kind":"string","required":true}]},"accountPasskeyRegistrationOptions":{"http":"POST","path":"/{version}/account/userauth/passkey/registration-options","dto":"AccountPasskeyRegistrationOptionsRequest","fields":[{"name":"verificationToken","type":"string","kind":"string","required":true}]},"accountVerifyPasskeyRegistration":{"http":"POST","path":"/{version}/account/userauth/passkey/verify-registration","dto":"AccountVerifyPasskeyRegistrationRequest","fields":[{"name":"verificationToken","type":"string","kind":"string","required":true},{"name":"ceremonyId","type":"string","kind":"string","required":true},{"name":"attestationResponse","type":"string","kind":"string","required":true},{"name":"friendlyName","type":"string","kind":"string","required":false}]},"accountPasskeyAuthenticationOptions":{"http":"POST","path":"/{version}/account/userauth/passkey/authentication-options","dto":"AccountPasskeyAuthenticationOptionsRequest","fields":[{"name":"email","type":"string","kind":"string","required":true}]},"accountVerifyPasskeyAuthentication":{"http":"POST","path":"/{version}/account/userauth/passkey/verify-authentication","dto":"AccountVerifyPasskeyAuthenticationRequest","fields":[{"name":"ceremonyId","type":"string","kind":"string","required":true},{"name":"assertionResponse","type":"string","kind":"string","required":true}]},"listAccountPasskeys":{"http":"GET","path":"/{version}/account/userauth/passkeys","dto":"ListAccountPasskeysRequest","fields":[]},"renameAccountPasskey":{"http":"POST","path":"/{version}/account/userauth/passkeys/{CredentialId}/rename","dto":"RenameAccountPasskeyRequest","fields":[{"name":"credentialId","type":"string","kind":"string","required":true},{"name":"friendlyName","type":"string","kind":"string","required":true}]},"revokeAccountPasskey":{"http":"POST","path":"/{version}/account/userauth/passkeys/{CredentialId}/revoke","dto":"RevokeAccountPasskeyRequest","fields":[{"name":"credentialId","type":"string","kind":"string","required":true}]},"accountPasskeyEnrollmentOptions":{"http":"POST","path":"/{version}/account/userauth/passkey/enrollment-options","dto":"AccountPasskeyEnrollmentOptionsRequest","fields":[]},"accountVerifyPasskeyEnrollment":{"http":"POST","path":"/{version}/account/userauth/passkey/verify-enrollment","dto":"AccountVerifyPasskeyEnrollmentRequest","fields":[{"name":"ceremonyId","type":"string","kind":"string","required":true},{"name":"attestationResponse","type":"string","kind":"string","required":true},{"name":"friendlyName","type":"string","kind":"string","required":false}]},"getLicenseDomainDnsStatus":{"http":"GET","path":"/{version}/account/licensing/dns-status","dto":"GetLicenseDomainDnsStatus","fields":[{"name":"domain","type":"string","kind":"string","required":false}]},"getLicenses":{"http":"GET","path":"/{version}/account/licenses","dto":"GetLicenses","fields":[]},"getInstallationLicenseStatus":{"http":"GET","path":"/{version}/account/licensing/status","dto":"GetInstallationLicenseStatus","fields":[]},"getAiTools":{"http":"GET","path":"/{version}/account/ai/tools","dto":"GetAiToolsRequest","fields":[{"name":"toolset","type":"string","kind":"string","required":false}]},"invokeAiTool":{"http":"POST","path":"/{version}/account/ai/tools/{ToolName}","dto":"InvokeAiToolRequest","fields":[{"name":"toolName","type":"string","kind":"string","required":true},{"name":"argumentsJson","type":"string","kind":"string","required":false}]},"askChat":{"http":"POST","path":"/{version}/account/chat/complete","dto":"AskChatRequest","fields":[{"name":"prompt","type":"string","kind":"string","required":true},{"name":"profile","type":"string","kind":"string","required":false}]},"uploadChatAttachment":{"http":"POST","path":"/{version}/account/chat/attachments","dto":"UploadChatAttachmentRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":false},{"name":"fileName","type":"string","kind":"string","required":true},{"name":"contentType","type":"string","kind":"string","required":true},{"name":"base64Content","type":"string","kind":"string","required":true},{"name":"profile","type":"string","kind":"string","required":false},{"name":"topic","type":"string","kind":"string","required":false}]},"chatAvailability":{"http":"GET","path":"/{version}/account/chat/availability","dto":"ChatAvailabilityRequest","fields":[]},"getChatMemory":{"http":"GET","path":"/{version}/account/chat/memory","dto":"GetChatMemoryRequest","fields":[]},"forgetChatMemory":{"http":"DELETE","path":"/{version}/account/chat/memory/{NoteId}","dto":"ForgetChatMemoryRequest","fields":[{"name":"noteId","type":"string","kind":"string","required":true}]},"deleteChatSession":{"http":"DELETE","path":"/{version}/account/chat/sessions/{SessionId}","dto":"DeleteChatSessionRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true}]},"setChatSessionArchived":{"http":"PATCH","path":"/{version}/account/chat/sessions/{SessionId}/archive","dto":"SetChatSessionArchivedRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true},{"name":"archived","type":"boolean","kind":"boolean","required":true}]},"setChatSessionPinned":{"http":"PATCH","path":"/{version}/account/chat/sessions/{SessionId}/pin","dto":"SetChatSessionPinnedRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true},{"name":"pinned","type":"boolean","kind":"boolean","required":true}]},"setChatSessionSharing":{"http":"PATCH","path":"/{version}/account/chat/sessions/{SessionId}/sharing","dto":"SetChatSessionSharingRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true},{"name":"doNotShare","type":"boolean","kind":"boolean","required":true}]},"getChatSessions":{"http":"GET","path":"/{version}/account/chat/sessions","dto":"GetChatSessionsRequest","fields":[{"name":"take","type":"number","kind":"number","required":false},{"name":"includeArchived","type":"boolean","kind":"boolean","required":false}]},"getChatSessionEntries":{"http":"GET","path":"/{version}/account/chat/sessions/{SessionId}/entries","dto":"GetChatSessionEntriesRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true},{"name":"sinceSeq","type":"number","kind":"number","required":false}]},"setChatEntryFeedback":{"http":"POST","path":"/{version}/account/chat/sessions/{SessionId}/entries/{EntryId}/feedback","dto":"SetChatEntryFeedbackRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true},{"name":"entryId","type":"string","kind":"string","required":true},{"name":"feedback","type":"string","kind":"string","required":false}]},"answerChatQuestion":{"http":"POST","path":"/{version}/account/chat/sessions/{SessionId}/questions/{EntryId}/answer","dto":"AnswerChatQuestionRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true},{"name":"entryId","type":"string","kind":"string","required":true}]},"decideChatPlan":{"http":"POST","path":"/{version}/account/chat/sessions/{SessionId}/plans/{EntryId}/decision","dto":"DecideChatPlanRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true},{"name":"entryId","type":"string","kind":"string","required":true},{"name":"decision","type":"string","kind":"string","required":true},{"name":"comment","type":"string","kind":"string","required":false}]},"stopChatRunStep":{"http":"POST","path":"/{version}/account/chat/sessions/{SessionId}/steps/{EntryId}/stop","dto":"StopChatRunStepRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":true},{"name":"entryId","type":"string","kind":"string","required":true}]},"chatTurn":{"http":"POST","path":"/{version}/account/chat/turn","dto":"ChatTurnRequest","fields":[{"name":"sessionId","type":"string","kind":"string","required":false},{"name":"message","type":"string","kind":"string","required":true},{"name":"profile","type":"string","kind":"string","required":false},{"name":"topic","type":"string","kind":"string","required":false},{"name":"llmIntegrationId","type":"string","kind":"string","required":false},{"name":"model","type":"string","kind":"string","required":false},{"name":"screenContext","type":"ChatScreenContextDto","kind":"json","required":false}]},"mcp":{"http":"POST","path":"/{version}/account/mcp","dto":"McpRequest","fields":[{"name":"requestStream","type":"string","kind":"string","required":true}]}},"ai":{"deleteLlmIntegration":{"http":"DELETE","path":"/{version}/ai/integrations/llms/{Id}","dto":"DeleteLlmIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableLlmIntegration":{"http":"PUT","path":"/{version}/ai/integrations/llms/{Id}/disable","dto":"DisableLlmIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableLlmIntegration":{"http":"PUT","path":"/{version}/ai/integrations/llms/{Id}/enable","dto":"EnableLlmIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getLlmIntegration":{"http":"GET","path":"/{version}/ai/integrations/llms/{id}","dto":"GetLlmIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getLlmIntegrations":{"http":"GET","path":"/{version}/ai/integrations/llms/integrations","dto":"GetLlmIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveLlmIntegration":{"http":"POST","path":"/{version}/ai/integrations/llms/","dto":"SaveLlmIntegration","fields":[{"name":"integration","type":"LlmIntegrationRequest","kind":"json","required":true}]},"testLlmIntegration":{"http":"POST","path":"/{version}/ai/integrations/llms/test","dto":"TestLlmIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"deleteMcpIntegration":{"http":"DELETE","path":"/{version}/ai/integrations/mcp/{Id}","dto":"DeleteMcpIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableMcpIntegration":{"http":"PUT","path":"/{version}/ai/integrations/mcp/{Id}/disable","dto":"DisableMcpIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableMcpIntegration":{"http":"PUT","path":"/{version}/ai/integrations/mcp/{Id}/enable","dto":"EnableMcpIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getMcpIntegration":{"http":"GET","path":"/{version}/ai/integrations/mcp/{id}","dto":"GetMcpIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getMcpIntegrations":{"http":"GET","path":"/{version}/ai/integrations/mcp/integrations","dto":"GetMcpIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveMcpIntegration":{"http":"POST","path":"/{version}/ai/integrations/mcp/","dto":"SaveMcpIntegration","fields":[{"name":"integration","type":"McpIntegrationRequest","kind":"json","required":true}]},"testMcpIntegration":{"http":"POST","path":"/{version}/ai/integrations/mcp/test","dto":"TestMcpIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]}},"apikeys":{},"auth":{},"code":{"enableCode":{"http":"GET","path":"/{version}/code/enable","dto":"EnableCode","fields":[]},"disableCode":{"http":"GET","path":"/{version}/code/disable","dto":"DisableCode","fields":[]},"getCodeIntegrations":{"http":"GET","path":"/{version}/code/integrations","dto":"GetCodeIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"getCodeIntegration":{"http":"GET","path":"/{version}/code/integrations/{id}","dto":"GetCodeIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"saveCodeIntegration":{"http":"POST","path":"/{version}/code/integrations","dto":"SaveCodeIntegration","fields":[{"name":"integration","type":"CodeIntegrationRequest","kind":"json","required":true}]},"testCodeIntegration":{"http":"POST","path":"/{version}/code/integrations/test","dto":"TestCodeIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"confirmCodeIntegrationHumanDelivery":{"http":"POST","path":"/{version}/code/integrations/confirm-human-delivery","dto":"ConfirmCodeIntegrationHumanDeliveryRequest","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"setCodeIntegrationAsDefault":{"http":"PUT","path":"/{version}/code/integrations/{Id}/default","dto":"SetCodeIntegrationAsDefault","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"deleteCodeIntegration":{"http":"DELETE","path":"/{version}/code/integrations/{Id}","dto":"DeleteCodeIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableCodeIntegration":{"http":"PUT","path":"/{version}/code/integrations/{Id}/enable","dto":"EnableCodeIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableCodeIntegration":{"http":"PUT","path":"/{version}/code/integrations/{Id}/disable","dto":"DisableCodeIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getMarketplaceListings":{"http":"GET","path":"/{version}/code/marketplace/listings","dto":"GetMarketplaceListings","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"categories","type":"MarketplaceCategory[]","kind":"string[]","required":false,"options":["Other","Crm","Erp","Marketing","Communication","Productivity","Storage","Analytics","Identity","Payments","DevTools","Ai","Files","Database","Calendar"]},{"name":"transports","type":"MarketplaceTransport[]","kind":"string[]","required":false,"options":["Mcp","Rest","Code","Internal","Sdk"]},{"name":"search","type":"string","kind":"string","required":false},{"name":"officialOnly","type":"boolean","kind":"boolean","required":false},{"name":"tags","type":"string[]","kind":"string[]","required":false}]},"getMarketplaceListingFunctionTokens":{"http":"GET","path":"/{version}/code/marketplace/listings/{ListingViewId}/functions/{FunctionKey}/tokens","dto":"GetMarketplaceListingFunctionTokens","fields":[{"name":"listingViewId","type":"string","kind":"string","required":true},{"name":"functionKey","type":"string","kind":"string","required":true}]},"getMarketplaceIntegrations":{"http":"GET","path":"/{version}/code/marketplace/integrations","dto":"GetMarketplaceIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"getMarketplaceIntegration":{"http":"GET","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}","dto":"GetMarketplaceIntegration","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true}]},"saveMarketplaceIntegration":{"http":"POST","path":"/{version}/code/marketplace/integrations","dto":"SaveMarketplaceIntegration","fields":[{"name":"integration","type":"MarketplaceIntegrationDto","kind":"json","required":true}]},"deleteMarketplaceIntegration":{"http":"DELETE","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}","dto":"DeleteMarketplaceIntegration","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true}]},"enableMarketplaceIntegration":{"http":"POST","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/enable","dto":"EnableMarketplaceIntegration","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true}]},"disableMarketplaceIntegration":{"http":"POST","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/disable","dto":"DisableMarketplaceIntegration","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true}]},"getMarketplaceFunctions":{"http":"GET","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/functions","dto":"GetMarketplaceFunctions","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"integrationViewId","type":"string","kind":"string","required":true}]},"getMarketplaceFunction":{"http":"GET","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/functions/{FunctionViewId}","dto":"GetMarketplaceFunction","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true},{"name":"functionViewId","type":"string","kind":"string","required":true}]},"saveMarketplaceFunction":{"http":"POST","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/functions","dto":"SaveMarketplaceFunction","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true},{"name":"function","type":"MarketplaceFunctionDto","kind":"json","required":true}]},"deleteMarketplaceFunction":{"http":"DELETE","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/functions/{FunctionViewId}","dto":"DeleteMarketplaceFunction","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true},{"name":"functionViewId","type":"string","kind":"string","required":true}]},"enableMarketplaceFunction":{"http":"POST","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/functions/{FunctionViewId}/enable","dto":"EnableMarketplaceFunction","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true},{"name":"functionViewId","type":"string","kind":"string","required":true}]},"disableMarketplaceFunction":{"http":"POST","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/functions/{FunctionViewId}/disable","dto":"DisableMarketplaceFunction","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true},{"name":"functionViewId","type":"string","kind":"string","required":true}]},"getMarketplaceFunctionTokens":{"http":"GET","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/functions/{FunctionViewId}/tokens","dto":"GetMarketplaceFunctionTokens","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true},{"name":"functionViewId","type":"string","kind":"string","required":true}]},"invokeMarketplaceFunction":{"http":"POST","path":"/{version}/code/marketplace/functions/{FunctionViewId}/invoke","dto":"InvokeMarketplaceFunction","fields":[{"name":"functionViewId","type":"string","kind":"string","required":true}]},"getMarketplaceListing":{"http":"GET","path":"/{version}/code/marketplace/listings/{ListingViewId}","dto":"GetMarketplaceListing","fields":[{"name":"listingViewId","type":"string","kind":"string","required":true}]},"testMarketplaceIntegration":{"http":"POST","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/test","dto":"TestMarketplaceIntegration","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true}]},"replaceMarketplaceIntegrationSecrets":{"http":"PUT","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/secrets","dto":"ReplaceMarketplaceIntegrationSecretsRequest","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true}]},"revealMarketplaceIntegrationSecrets":{"http":"POST","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/secrets/reveal","dto":"RevealMarketplaceIntegrationSecretsRequest","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true}]},"setMarketplaceIntegrationTokenMappings":{"http":"PUT","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/token-mappings","dto":"SetMarketplaceIntegrationTokenMappingsRequest","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true},{"name":"tokenMappings","type":"MarketplaceTokenMappingDto[]","kind":"json[]","required":true}]},"getMarketplaceFunctionCatalog":{"http":"GET","path":"/{version}/code/marketplace/integrations/{IntegrationViewId}/catalog","dto":"GetMarketplaceFunctionCatalog","fields":[{"name":"integrationViewId","type":"string","kind":"string","required":true}]}},"compliance":{"getComplianceSettings":{"http":"GET","path":"/{version}/compliance/settings","dto":"GetComplianceSettings","fields":[]},"removeRetentionWindow":{"http":"DELETE","path":"/{version}/compliance/retention","dto":"RemoveRetentionWindowRequest","fields":[{"name":"dataKind","type":"string","kind":"string","required":true}]},"saveRetentionWindow":{"http":"POST","path":"/{version}/compliance/retention","dto":"SaveRetentionWindowRequest","fields":[{"name":"dataKind","type":"string","kind":"string","required":true},{"name":"days","type":"number","kind":"number","required":true},{"name":"action","type":"string","kind":"string","required":true}]},"assignRegime":{"http":"POST","path":"/{version}/compliance/regimes","dto":"AssignRegimeRequest","fields":[{"name":"regime","type":"string","kind":"string","required":true}]},"clearRegime":{"http":"DELETE","path":"/{version}/compliance/regimes","dto":"ClearRegimeRequest","fields":[{"name":"regime","type":"string","kind":"string","required":true}]},"defineConsentPurpose":{"http":"POST","path":"/{version}/compliance/purposes","dto":"DefineConsentPurposeRequest","fields":[{"name":"key","type":"string","kind":"string","required":true},{"name":"name","type":"string","kind":"string","required":true},{"name":"channel","type":"string","kind":"string","required":true},{"name":"mappedTags","type":"string[]","kind":"string[]","required":true},{"name":"regulatoryBasis","type":"string[]","kind":"string[]","required":true},{"name":"description","type":"string","kind":"string","required":false}]},"deprecateConsentPurpose":{"http":"POST","path":"/{version}/compliance/purposes/deprecate","dto":"DeprecateConsentPurposeRequest","fields":[{"name":"key","type":"string","kind":"string","required":true}]},"getLegalHolds":{"http":"GET","path":"/{version}/compliance/holds","dto":"GetLegalHolds","fields":[]},"placeLegalHold":{"http":"POST","path":"/{version}/compliance/holds","dto":"PlaceLegalHoldRequest","fields":[{"name":"subjectKind","type":"string","kind":"string","required":true},{"name":"subjectId","type":"string","kind":"string","required":true},{"name":"reason","type":"string","kind":"string","required":true}]},"releaseLegalHold":{"http":"POST","path":"/{version}/compliance/holds/release","dto":"ReleaseLegalHoldRequest","fields":[{"name":"holdId","type":"string","kind":"string","required":true}]},"approveDsarRequest":{"http":"POST","path":"/{version}/compliance/dsar/approve","dto":"ApproveDsarRequestRequest","fields":[{"name":"requestId","type":"string","kind":"string","required":true}]},"rejectDsarRequest":{"http":"POST","path":"/{version}/compliance/dsar/reject","dto":"RejectDsarRequestRequest","fields":[{"name":"requestId","type":"string","kind":"string","required":true},{"name":"reason","type":"string","kind":"string","required":true}]},"getDsarRequests":{"http":"GET","path":"/{version}/compliance/dsar","dto":"GetDsarRequests","fields":[]},"openDsarRequest":{"http":"POST","path":"/{version}/compliance/dsar","dto":"OpenDsarRequestRequest","fields":[{"name":"subjectKind","type":"string","kind":"string","required":true},{"name":"subjectId","type":"string","kind":"string","required":true}]},"getComplianceAuditLog":{"http":"GET","path":"/{version}/compliance/audit","dto":"GetComplianceAuditLog","fields":[{"name":"from","type":"string","kind":"string","required":false},{"name":"to","type":"string","kind":"string","required":false},{"name":"subjectKind","type":"string","kind":"string","required":false},{"name":"subjectId","type":"string","kind":"string","required":false},{"name":"limit","type":"number","kind":"number","required":true}]},"getAccountCompliance":{"http":"GET","path":"/{version}/compliance/account","dto":"GetAccountCompliance","fields":[]},"saveDsarPolicy":{"http":"POST","path":"/{version}/compliance/account/dsar-policy","dto":"SaveDsarPolicyRequest","fields":[{"name":"mode","type":"string","kind":"string","required":true},{"name":"delayDays","type":"number","kind":"number","required":true}]},"saveIncidentRouting":{"http":"POST","path":"/{version}/compliance/account/incident-routing","dto":"SaveIncidentRoutingRequest","fields":[{"name":"autoForwardAdvisories","type":"boolean","kind":"boolean","required":true},{"name":"securityContact","type":"string","kind":"string","required":false}]}},"database":{"disableDatabase":{"http":"GET","path":"/{version}/database/disable","dto":"DisableDatabase","fields":[]},"enableDatabase":{"http":"GET","path":"/{version}/database/enable","dto":"EnableDatabase","fields":[]},"deleteSchemaTrigger":{"http":"DELETE","path":"/{version}/database/schemas/triggers/{triggerId}","dto":"DeleteSchemaTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"disableSchemaTrigger":{"http":"PATCH","path":"/{version}/database/schemas/triggers/{triggerId}/disable","dto":"DisableSchemaTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"enableSchemaTrigger":{"http":"PATCH","path":"/{version}/database/schemas/triggers/{triggerId}/enable","dto":"EnableSchemaTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"getSchemaTrigger":{"http":"GET","path":"/{version}/database/schemas/triggers/{id}","dto":"GetSchemaTrigger","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"schemaId","type":"string","kind":"string","required":false}]},"getSchemaTriggers":{"http":"GET","path":"/{version}/database/schemas/triggers","dto":"GetSchemaTriggers","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"schemaId","type":"string","kind":"string","required":false}]},"saveSchemaTrigger":{"http":"POST","path":"/{version}/database/schemas/triggers","dto":"SaveSchemaTrigger","fields":[{"name":"trigger","type":"SaveTriggerRequest","kind":"json","required":true}]},"deleteDatabaseTaxonomy":{"http":"DELETE","path":"/{version}/database/taxonomies/{Id}","dto":"DeleteDatabaseTaxonomyRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseTaxonomy":{"http":"GET","path":"/{version}/database/taxonomies/{id}","dto":"GetDatabaseTaxonomy","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseTaxonomies":{"http":"GET","path":"/{version}/database/taxonomies","dto":"GetDatabaseTaxonomies","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"getDatabaseTaxonomyTree":{"http":"GET","path":"/{version}/database/taxonomies/tree","dto":"GetDatabaseTaxonomyTreeRequest","fields":[{"name":"includeTerms","type":"boolean","kind":"boolean","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"saveDatabaseTaxonomy":{"http":"POST","path":"/{version}/database/taxonomies","dto":"SaveDatabaseTaxonomyRequest","fields":[{"name":"viewId","type":"string","kind":"string","required":false},{"name":"taxonomyName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"termsMetaDataSchema","type":"string","kind":"string","required":false},{"name":"termsMetaVisualSchema","type":"string","kind":"string","required":false},{"name":"parentId","type":"string","kind":"string","required":false},{"name":"dependencies","type":"string[]","kind":"string[]","required":false}]},"deleteDatabaseTaxonomyTerm":{"http":"DELETE","path":"/{version}/database/taxonomies/{TaxonomyId}/terms/{Id}","dto":"DeleteDatabaseTaxonomyTermRequest","fields":[{"name":"taxonomyId","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"deleteManyDatabaseTaxonomyTerms":{"http":"DELETE","path":"/{version}/database/taxonomies/{TaxonomyId}/terms/many","dto":"DeleteManyDatabaseTaxonomyTermsRequest","fields":[{"name":"taxonomyId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":true}]},"getDatabaseTaxonomyTerm":{"http":"GET","path":"/{version}/database/taxonomies/{TaxonomyId}/terms/{Id}","dto":"GetDatabaseTaxonomyTermRequest","fields":[{"name":"taxonomyId","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getDatabaseMergedTermTree":{"http":"GET","path":"/{version}/database/taxonomies/{TaxonomyName}/merged-tree","dto":"GetDatabaseMergedTermTreeRequest","fields":[{"name":"taxonomyName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getDatabaseTaxonomyTermTree":{"http":"GET","path":"/{version}/database/taxonomies/{TaxonomyName}/terms/tree","dto":"GetDatabaseTaxonomyTermTreeRequest","fields":[{"name":"taxonomyName","type":"string","kind":"string","required":true},{"name":"rootTermId","type":"string","kind":"string","required":false},{"name":"depth","type":"number","kind":"number","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"saveDatabaseTaxonomyTerm":{"http":"POST","path":"/{version}/database/taxonomies/{TaxonomyId}/terms","dto":"SaveDatabaseTaxonomyTermRequest","fields":[{"name":"taxonomyId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"document","type":"string","kind":"string","required":true}]},"updateDatabaseTaxonomyTerm":{"http":"PUT","path":"/{version}/database/taxonomies/{TaxonomyId}/terms/{Id}","dto":"UpdateDatabaseTaxonomyTermRequest","fields":[{"name":"taxonomyId","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"update","type":"string","kind":"string","required":true}]},"applyDatabaseSchemaBundle":{"http":"POST","path":"/{version}/database/schemas/apply-bundle","dto":"ApplyDatabaseSchemaBundleRequest","fields":[{"name":"entities","type":"string","kind":"string","required":false},{"name":"bundleJson","type":"string","kind":"string","required":false},{"name":"tier","type":"string","kind":"string","required":false},{"name":"translatable","type":"boolean","kind":"boolean","required":true},{"name":"publish","type":"boolean","kind":"boolean","required":true}]},"deleteDatabaseSchema":{"http":"DELETE","path":"/{version}/database/schemas/{Id}","dto":"DeleteDatabaseSchemaRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"discardDatabaseSchemaDraft":{"http":"DELETE","path":"/{version}/database/schemas/{Id}/draft","dto":"DiscardDatabaseSchemaDraftRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseSchema":{"http":"GET","path":"/{version}/database/schemas/{id}","dto":"GetDatabaseSchema","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseSchemas":{"http":"GET","path":"/{version}/database/schemas","dto":"GetDatabaseSchemas","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"getDatabaseSchemaDraft":{"http":"GET","path":"/{version}/database/schemas/{Id}/draft","dto":"GetDatabaseSchemaDraft","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseSchemaListSettings":{"http":"GET","path":"/{version}/database/schemas/{Id}/list-settings","dto":"GetDatabaseSchemaListSettings","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseSchemaVersionDiff":{"http":"GET","path":"/{version}/database/schemas/{Id}/versions/diff","dto":"GetDatabaseSchemaVersionDiff","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"fromVersion","type":"number","kind":"number","required":true},{"name":"toVersion","type":"number","kind":"number","required":true}]},"getDatabaseSchemaVersions":{"http":"GET","path":"/{version}/database/schemas/{Id}/versions","dto":"GetDatabaseSchemaVersions","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"publishDatabaseSchema":{"http":"POST","path":"/{version}/database/schemas/{Id}/publish","dto":"PublishDatabaseSchemaRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"confirmed","type":"boolean","kind":"boolean","required":true}]},"renameDatabaseSchema":{"http":"PUT","path":"/{version}/database/schemas/{Id}/rename","dto":"RenameDatabaseSchemaRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"title","type":"string","kind":"string","required":true},{"name":"renameUniqueName","type":"boolean","kind":"boolean","required":true}]},"saveDatabaseSchema":{"http":"POST","path":"/{version}/database/schemas","dto":"SaveDatabaseSchemaRequest","fields":[{"name":"viewId","type":"string","kind":"string","required":false},{"name":"schemaName","type":"string","kind":"string","required":true},{"name":"dataSchema","type":"string","kind":"string","required":false},{"name":"visualSchema","type":"string","kind":"string","required":false},{"name":"settings","type":"SchemaSettingsDto","kind":"json","required":false}]},"updateDatabaseSchemaDraft":{"http":"PUT","path":"/{version}/database/schemas/{Id}/draft","dto":"UpdateDatabaseSchemaDraftRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"dataSchema","type":"string","kind":"string","required":false},{"name":"visualSchema","type":"string","kind":"string","required":false}]},"updateDatabaseSchemaListSettings":{"http":"PUT","path":"/{version}/database/schemas/{Id}/list-settings","dto":"UpdateDatabaseSchemaListSettingsRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"settings","type":"SchemaListSettingsDto","kind":"json","required":true}]},"updateDatabaseSchemaSettings":{"http":"PUT","path":"/{version}/database/schemas/{Id}/settings","dto":"UpdateDatabaseSchemaSettingsRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"settings","type":"SchemaSettingsDto","kind":"json","required":true}]},"aggregateRecords":{"http":"POST","path":"/{version}/database/collections/{collectionName}/aggregate","dto":"AggregateRecords","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"pipeline","type":"string","kind":"string","required":true}]},"changeRecordResponsibility":{"http":"PUT","path":"/{version}/database/collections/{collectionName}/{id}/responsibility","dto":"ChangeRecordResponsibility","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"newResponsibleUserId","type":"string","kind":"string","required":true}]},"countRecords":{"http":"GET","path":"/{version}/database/collections/{collectionName}/count","dto":"CountRecords","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":false},{"name":"schemaVersion","type":"number","kind":"number","required":false}]},"deleteManyRecords":{"http":"DELETE","path":"/{version}/database/collections/{collectionName}/many","dto":"DeleteManyRecords","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":true}]},"deleteRecord":{"http":"DELETE","path":"/{version}/database/collections/{collectionName}/{id}","dto":"DeleteRecord","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"distinctRecordValues":{"http":"GET","path":"/{version}/database/collections/{collectionName}/distinct","dto":"DistinctRecordValues","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"field","type":"string","kind":"string","required":true},{"name":"filter","type":"string","kind":"string","required":false},{"name":"schemaVersion","type":"number","kind":"number","required":false}]},"executeRecordsAggregate":{"http":"POST","path":"/{version}/database/collections/{collectionName}/aggregates/{aggregateId}/execute","dto":"ExecuteRecordsAggregate","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"aggregateId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"findRecords":{"http":"GET","path":"/{version}/database/collections/{collectionName}","dto":"FindRecords","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":false},{"name":"contactId","type":"string","kind":"string","required":false},{"name":"schemaVersion","type":"number","kind":"number","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false},{"name":"sortBy","type":"string","kind":"string","required":false},{"name":"sortOrder","type":"number","kind":"number","required":false}]},"findOneRecord":{"http":"GET","path":"/{version}/database/collections/{collectionName}/{id}","dto":"FindOneRecord","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getCollectionIndexes":{"http":"GET","path":"/{version}/database/collections/{collectionName}/indexes","dto":"GetCollectionIndexes","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"insertManyRecords":{"http":"POST","path":"/{version}/database/collections/{collectionName}/many","dto":"InsertManyRecords","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"documents","type":"string","kind":"string","required":true}]},"insertRecord":{"http":"POST","path":"/{version}/database/collections/{collectionName}","dto":"InsertRecord","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"document","type":"string","kind":"string","required":true}]},"replaceRecord":{"http":"PUT","path":"/{version}/database/collections/{collectionName}/{id}/replace","dto":"ReplaceRecord","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"replacement","type":"string","kind":"string","required":true}]},"seedCollectionRecords":{"http":"POST","path":"/{version}/database/collections/seed","dto":"SeedCollectionRecords","fields":[{"name":"mode","type":"string","kind":"string","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"collections","type":"string","kind":"string","required":true}]},"updateManyRecords":{"http":"PUT","path":"/{version}/database/collections/{collectionName}/many","dto":"UpdateManyRecords","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"filter","type":"string","kind":"string","required":true},{"name":"update","type":"string","kind":"string","required":true}]},"updateOneRecord":{"http":"PUT","path":"/{version}/database/collections/{collectionName}/{id}","dto":"UpdateOneRecord","fields":[{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"update","type":"string","kind":"string","required":true}]},"deleteDatabaseIntegration":{"http":"DELETE","path":"/{version}/database/integrations/{Id}","dto":"DeleteDatabaseIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableDatabaseIntegration":{"http":"PUT","path":"/{version}/database/integrations/{Id}/disable","dto":"DisableDatabaseIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableDatabaseIntegration":{"http":"PUT","path":"/{version}/database/integrations/{Id}/enable","dto":"EnableDatabaseIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseIntegration":{"http":"GET","path":"/{version}/database/integrations/{id}","dto":"GetDatabaseIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getDatabaseIntegrations":{"http":"GET","path":"/{version}/database/integrations","dto":"GetDatabaseIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"getAllowedFlexTiers":{"http":"GET","path":"/{version}/database/integrations/flex-tiers","dto":"GetAllowedFlexTiers","fields":[]},"revealManagedFlexConnectionString":{"http":"GET","path":"/{version}/database/integrations/{Id}/connection-string","dto":"RevealManagedFlexConnectionString","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"saveDatabaseIntegration":{"http":"POST","path":"/{version}/database/integrations","dto":"SaveDatabaseIntegration","fields":[{"name":"integration","type":"DatabaseIntegrationRequest","kind":"json","required":true}]},"setDatabaseIntegrationAsDefault":{"http":"PUT","path":"/{version}/database/integrations/{Id}/default","dto":"SetDatabaseIntegrationAsDefaultRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"testDatabaseIntegration":{"http":"POST","path":"/{version}/database/integrations/test","dto":"TestDatabaseIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"createCollectionImport":{"http":"POST","path":"/{version}/database/imports","dto":"CreateCollectionImport","fields":[{"name":"file","type":"FileResourceRefDto","kind":"json","required":true},{"name":"schemaId","type":"string","kind":"string","required":true},{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"delimiter","type":"string","kind":"string","required":true},{"name":"hasHeader","type":"boolean","kind":"boolean","required":true},{"name":"mapping","type":"ImportColumnMappingDto[]","kind":"json[]","required":true}]},"deleteCollectionImport":{"http":"DELETE","path":"/{version}/database/imports/{Id}","dto":"DeleteCollectionImportRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getCollectionImport":{"http":"GET","path":"/{version}/database/imports/{Id}","dto":"GetCollectionImport","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getCollectionImports":{"http":"GET","path":"/{version}/database/imports","dto":"GetCollectionImports","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"requestImportUploadUrl":{"http":"POST","path":"/{version}/database/imports/upload-url","dto":"RequestImportUploadUrlRequest","fields":[{"name":"fileAccountId","type":"string","kind":"string","required":false},{"name":"fileName","type":"string","kind":"string","required":true}]},"analyzeImportFile":{"http":"POST","path":"/{version}/database/imports/analyze","dto":"AnalyzeImportFileRequest","fields":[{"name":"file","type":"FileResourceRefDto","kind":"json","required":true},{"name":"delimiter","type":"string","kind":"string","required":true},{"name":"hasHeader","type":"boolean","kind":"boolean","required":true}]},"deleteDatabaseAggregate":{"http":"DELETE","path":"/{version}/database/aggregates/{Id}","dto":"DeleteDatabaseAggregateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"schemaId","type":"string","kind":"string","required":true}]},"getDatabaseAggregate":{"http":"GET","path":"/{version}/database/aggregates/{Id}","dto":"GetDatabaseAggregate","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"schemaId","type":"string","kind":"string","required":true}]},"getDatabaseAggregates":{"http":"GET","path":"/{version}/database/aggregates","dto":"GetDatabaseAggregates","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"schemaId","type":"string","kind":"string","required":true},{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"saveDatabaseAggregate":{"http":"POST","path":"/{version}/database/aggregates","dto":"SaveDatabaseAggregateRequest","fields":[{"name":"viewId","type":"string","kind":"string","required":false},{"name":"schemaId","type":"string","kind":"string","required":true},{"name":"displayName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"pipeline","type":"string","kind":"string","required":true}]},"testDatabaseAggregate":{"http":"POST","path":"/{version}/database/aggregates/test","dto":"TestDatabaseAggregateRequest","fields":[{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"collectionName","type":"string","kind":"string","required":true},{"name":"pipeline","type":"string","kind":"string","required":true}]}},"diagnostics":{"getDiagnosticPacks":{"http":"GET","path":"/{version}/diagnostics/packs","dto":"GetDiagnosticPacks","fields":[]},"runDiagnosticPack":{"http":"POST","path":"/{version}/diagnostics/packs/{PackName}/run","dto":"RunDiagnosticPackRequest","fields":[{"name":"packName","type":"string","kind":"string","required":true},{"name":"packVersion","type":"number","kind":"number","required":false},{"name":"caseId","type":"string","kind":"string","required":false}]},"getDiagnosticEcho":{"http":"GET","path":"/{version}/diagnostics/echo","dto":"GetDiagnosticEcho","fields":[{"name":"caseId","type":"string","kind":"string","required":false}]},"readDiagnosticEvents":{"http":"GET","path":"/{version}/diagnostics/events","dto":"ReadDiagnosticEventsRequest","fields":[{"name":"stream","type":"string","kind":"string","required":true},{"name":"from","type":"number","kind":"number","required":true},{"name":"count","type":"number","kind":"number","required":true},{"name":"caseId","type":"string","kind":"string","required":false}]},"queryDiagnosticLogs":{"http":"GET","path":"/{version}/diagnostics/logs","dto":"QueryDiagnosticLogsRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"level","type":"string","kind":"string","required":false},{"name":"module","type":"string","kind":"string","required":false},{"name":"logCorrelationId","type":"string","kind":"string","required":false},{"name":"eventCode","type":"string","kind":"string","required":false},{"name":"search","type":"string","kind":"string","required":false},{"name":"fromUtc","type":"string","kind":"string","required":false},{"name":"toUtc","type":"string","kind":"string","required":false},{"name":"caseId","type":"string","kind":"string","required":false}]},"inspectDiagnosticRedis":{"http":"GET","path":"/{version}/diagnostics/redis","dto":"InspectDiagnosticRedisRequest","fields":[{"name":"keyPattern","type":"string","kind":"string","required":true},{"name":"caseId","type":"string","kind":"string","required":false}]},"runDiagnosticHealthCheck":{"http":"POST","path":"/{version}/diagnostics/health/{CheckId}","dto":"RunDiagnosticHealthCheckRequest","fields":[{"name":"checkId","type":"string","kind":"string","required":true},{"name":"caseId","type":"string","kind":"string","required":false}]}},"echo":{"echo":{"http":"GET","path":"/{version}/echo","dto":"Echo","fields":[]}},"email":{"oneClickUnsubscribe":{"http":"POST","path":"/{version}/email/one-click-unsubscribe","dto":"OneClickUnsubscribeRequest","fields":[{"name":"token","type":"string","kind":"string","required":true}]}},"environments":{},"files":{"disableFiles":{"http":"GET","path":"/{version}/files/disable","dto":"DisableFiles","fields":[]},"enableFiles":{"http":"GET","path":"/{version}/files/enable","dto":"EnableFiles","fields":[]},"deleteFilesTrigger":{"http":"DELETE","path":"/{version}/files/triggers/{triggerId}","dto":"DeleteFilesTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"disableFilesTrigger":{"http":"PATCH","path":"/{version}/files/triggers/{triggerId}/disable","dto":"DisableFilesTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"enableFilesTrigger":{"http":"PATCH","path":"/{version}/files/triggers/{triggerId}/enable","dto":"EnableFilesTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"getFilesTrigger":{"http":"GET","path":"/{version}/files/triggers/{id}","dto":"GetFilesTrigger","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"schemaId","type":"string","kind":"string","required":false}]},"getFilesTriggers":{"http":"GET","path":"/{version}/files/triggers","dto":"GetFilesTriggers","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"schemaId","type":"string","kind":"string","required":false}]},"saveFilesTrigger":{"http":"POST","path":"/{version}/files/triggers","dto":"SaveFilesTrigger","fields":[{"name":"trigger","type":"SaveTriggerRequest","kind":"json","required":true}]},"makeFilePrivate":{"http":"POST","path":"/{version}/files/item/private","dto":"MakeFilePrivateRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true}]},"makeFilePublic":{"http":"POST","path":"/{version}/files/item/public","dto":"MakeFilePublicRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true}]},"makeFolderPrivate":{"http":"POST","path":"/{version}/files/folder/private","dto":"MakeFolderPrivateRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true}]},"makeFolderPublic":{"http":"POST","path":"/{version}/files/folder/public","dto":"MakeFolderPublicRequest","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true}]},"deleteFilesIntegration":{"http":"DELETE","path":"/{version}/files/integrations/{Id}","dto":"DeleteFilesIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableFilesIntegration":{"http":"PUT","path":"/{version}/files/integrations/{Id}/disable","dto":"DisableFilesIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableFilesIntegration":{"http":"PUT","path":"/{version}/files/integrations/{Id}/enable","dto":"EnableFilesIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getFilesIntegration":{"http":"GET","path":"/{version}/files/integrations/{id}","dto":"GetFilesIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getFilesIntegrations":{"http":"GET","path":"/{version}/files/integrations","dto":"GetFilesIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveFilesIntegration":{"http":"POST","path":"/{version}/files/integrations","dto":"SaveFilesIntegration","fields":[{"name":"integration","type":"FilesIntegrationRequest","kind":"json","required":true}]},"setFilesIntegrationAsDefault":{"http":"PUT","path":"/{version}/files/integrations/{Id}/default","dto":"SetFilesIntegrationAsDefaultRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"testFilesIntegration":{"http":"POST","path":"/{version}/files/integrations/test","dto":"TestFilesIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"getFile":{"http":"GET","path":"/{version}/files/item","dto":"GetFile","fields":[{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":true}]},"getFolderFiles":{"http":"GET","path":"/{version}/files/folder","dto":"GetFolderFiles","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"filesIntegrationId","type":"string","kind":"string","required":true},{"name":"path","type":"string","kind":"string","required":false}]}},"internal":{"internalsTypeGen":{"http":"GET","path":"/internal/_typegen","dto":"InternalsTypeGen","fields":[{"name":"typegen_0_SmtpEmailIntegrationRequest","type":"SmtpEmailIntegrationRequest","kind":"json","required":false},{"name":"typegen_1_AwsSesEmailIntegrationRequest","type":"AwsSesEmailIntegrationRequest","kind":"json","required":false},{"name":"typegen_2_SendGridEmailIntegrationRequest","type":"SendGridEmailIntegrationRequest","kind":"json","required":false},{"name":"typegen_3_MailGunEmailIntegrationRequest","type":"MailGunEmailIntegrationRequest","kind":"json","required":false},{"name":"typegen_4_EmailToAllUsersDeliverySettingsRequest","type":"EmailToAllUsersDeliverySettingsRequest","kind":"json","required":false},{"name":"typegen_5_EmailToAccountUsersDeliverySettingsRequest","type":"EmailToAccountUsersDeliverySettingsRequest","kind":"json","required":false},{"name":"typegen_6_EmailToCollectionRecordsDeliverySettingsRequest","type":"EmailToCollectionRecordsDeliverySettingsRequest","kind":"json","required":false},{"name":"typegen_7_EmailToEmailsDeliverySettingsRequest","type":"EmailToEmailsDeliverySettingsRequest","kind":"json","required":false},{"name":"typegen_8_EmailToUsersDeliverySettingsRequest","type":"EmailToUsersDeliverySettingsRequest","kind":"json","required":false},{"name":"typegen_9_MembershipTriggerRequest","type":"MembershipTriggerRequest","kind":"json","required":false},{"name":"typegen_10_SchemaTriggerRequest","type":"SchemaTriggerRequest","kind":"json","required":false},{"name":"typegen_11_FilesTriggerRequest","type":"FilesTriggerRequest","kind":"json","required":false},{"name":"typegen_12_PaymentTriggerRequest","type":"PaymentTriggerRequest","kind":"json","required":false},{"name":"typegen_15_MongoDbConnectionStringDatabaseIntegrationRequest","type":"MongoDbConnectionStringDatabaseIntegrationRequest","kind":"json","required":false},{"name":"typegen_16_MongoDbAtlasFlexManagedDatabaseIntegrationRequest","type":"MongoDbAtlasFlexManagedDatabaseIntegrationRequest","kind":"json","required":false},{"name":"typegen_16_GoogleDriveFilesIntegrationRequest","type":"GoogleDriveFilesIntegrationRequest","kind":"json","required":false},{"name":"typegen_17_FtpFilesIntegrationRequest","type":"FtpFilesIntegrationRequest","kind":"json","required":false},{"name":"typegen_18_DropBoxFilesIntegrationRequest","type":"DropBoxFilesIntegrationRequest","kind":"json","required":false},{"name":"typegen_19_AppleICloudFilesIntegrationRequest","type":"AppleICloudFilesIntegrationRequest","kind":"json","required":false},{"name":"typegen_20_AwsS3FilesIntegrationRequest","type":"AwsS3FilesIntegrationRequest","kind":"json","required":false},{"name":"typegen_21_GoogleCloudFilesIntegrationRequest","type":"GoogleCloudFilesIntegrationRequest","kind":"json","required":false},{"name":"typegen_22_AzureBlobFilesIntegrationRequest","type":"AzureBlobFilesIntegrationRequest","kind":"json","required":false},{"name":"typegen_23_LocalFilesIntegrationRequest","type":"LocalFilesIntegrationRequest","kind":"json","required":false},{"name":"typegen_24_AmqpLoggingIntegrationRequest","type":"AmqpLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_25_AwsKinesisLoggingIntegrationRequest","type":"AwsKinesisLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_26_AwsS3LoggingIntegrationRequest","type":"AwsS3LoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_28_NewRelicLoggingIntegrationRequest","type":"NewRelicLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_30_MongoDbLoggingIntegrationRequest","type":"MongoDbLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_31_KafkaLoggingIntegrationRequest","type":"KafkaLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_32_PrometheusLoggingIntegrationRequest","type":"PrometheusLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_33_DataDogLoggingIntegrationRequest","type":"DataDogLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_34_InternalKafkaLoggingIntegrationRequest","type":"InternalKafkaLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_35_ElasticSearchLoggingIntegrationRequest","type":"ElasticSearchLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_37_SplunkLoggingIntegrationRequest","type":"SplunkLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_38_AzureOtelLoggingIntegrationRequest","type":"AzureOtelLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_39_KibanaLoggingIntegrationRequest","type":"KibanaLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_40_LocalFileLoggingIntegrationRequest","type":"LocalFileLoggingIntegrationRequest","kind":"json","required":false},{"name":"typegen_41_OktaMembershipIntegrationRequest","type":"OktaMembershipIntegrationRequest","kind":"json","required":false},{"name":"typegen_42_XMembershipIntegrationRequest","type":"XMembershipIntegrationRequest","kind":"json","required":false},{"name":"typegen_43_GoogleMembershipIntegrationRequest","type":"GoogleMembershipIntegrationRequest","kind":"json","required":false},{"name":"typegen_44_MicrosoftMembershipIntegrationRequest","type":"MicrosoftMembershipIntegrationRequest","kind":"json","required":false},{"name":"typegen_45_GitHubMembershipIntegrationRequest","type":"GitHubMembershipIntegrationRequest","kind":"json","required":false},{"name":"typegen_46_MetaMembershipIntegrationRequest","type":"MetaMembershipIntegrationRequest","kind":"json","required":false},{"name":"typegen_47_AppleMembershipIntegrationRequest","type":"AppleMembershipIntegrationRequest","kind":"json","required":false},{"name":"typegen_48_LemonSqueezyPaymentIntegrationRequest","type":"LemonSqueezyPaymentIntegrationRequest","kind":"json","required":false},{"name":"typegen_49_AdyenPaymentIntegrationRequest","type":"AdyenPaymentIntegrationRequest","kind":"json","required":false},{"name":"typegen_50_MolliePaymentIntegrationRequest","type":"MolliePaymentIntegrationRequest","kind":"json","required":false},{"name":"typegen_51_PaddlePaymentIntegrationRequest","type":"PaddlePaymentIntegrationRequest","kind":"json","required":false},{"name":"typegen_52_PayPalPaymentIntegrationRequest","type":"PayPalPaymentIntegrationRequest","kind":"json","required":false},{"name":"typegen_53_StripePaymentIntegrationRequest","type":"StripePaymentIntegrationRequest","kind":"json","required":false},{"name":"typegen_54_AppleInAppPaymentIntegrationRequest","type":"AppleInAppPaymentIntegrationRequest","kind":"json","required":false},{"name":"typegen_55_GoogleInAppPaymentIntegrationRequest","type":"GoogleInAppPaymentIntegrationRequest","kind":"json","required":false},{"name":"typegen_56_EdgeWebPushIntegrationRequest","type":"EdgeWebPushIntegrationRequest","kind":"json","required":false},{"name":"typegen_57_ChromePluginPushIntegrationRequest","type":"ChromePluginPushIntegrationRequest","kind":"json","required":false},{"name":"typegen_58_SafariPushIntegrationRequest","type":"SafariPushIntegrationRequest","kind":"json","required":false},{"name":"typegen_59_ChromeWebPushIntegrationRequest","type":"ChromeWebPushIntegrationRequest","kind":"json","required":false},{"name":"typegen_60_FirefoxWebPushIntegrationRequest","type":"FirefoxWebPushIntegrationRequest","kind":"json","required":false},{"name":"typegen_61_AndroidFirebasePushIntegrationRequest","type":"AndroidFirebasePushIntegrationRequest","kind":"json","required":false},{"name":"typegen_62_AppleApnsPushIntegrationRequest","type":"AppleApnsPushIntegrationRequest","kind":"json","required":false},{"name":"typegen_65_AwsLambdaCodeIntegrationRequest","type":"AwsLambdaCodeIntegrationRequest","kind":"json","required":false},{"name":"typegen_66_AzureFunctionsCodeIntegrationRequest","type":"AzureFunctionsCodeIntegrationRequest","kind":"json","required":false},{"name":"typegen_67_GoogleCloudFunctionsCodeIntegrationRequest","type":"GoogleCloudFunctionsCodeIntegrationRequest","kind":"json","required":false},{"name":"typegen_68_OllamaLlmIntegrationRequest","type":"OllamaLlmIntegrationRequest","kind":"json","required":false},{"name":"typegen_69_OpenRouterLlmIntegrationRequest","type":"OpenRouterLlmIntegrationRequest","kind":"json","required":false},{"name":"typegen_70_MistralLlmIntegrationRequest","type":"MistralLlmIntegrationRequest","kind":"json","required":false},{"name":"typegen_71_GrokLlmIntegrationRequest","type":"GrokLlmIntegrationRequest","kind":"json","required":false},{"name":"typegen_72_GroqLlmIntegrationRequest","type":"GroqLlmIntegrationRequest","kind":"json","required":false},{"name":"typegen_73_GoogleLlmIntegrationRequest","type":"GoogleLlmIntegrationRequest","kind":"json","required":false},{"name":"typegen_74_AnthropicLlmIntegrationRequest","type":"AnthropicLlmIntegrationRequest","kind":"json","required":false},{"name":"typegen_75_OpenAiLlmIntegrationRequest","type":"OpenAiLlmIntegrationRequest","kind":"json","required":false},{"name":"typegen_76_PlaywrightMcpIntegrationRequest","type":"PlaywrightMcpIntegrationRequest","kind":"json","required":false},{"name":"typegen_77_MongoDbMcpIntegrationRequest","type":"MongoDbMcpIntegrationRequest","kind":"json","required":false},{"name":"typegen_78_GitHubMcpIntegrationRequest","type":"GitHubMcpIntegrationRequest","kind":"json","required":false},{"name":"typegen_79_StripeMcpIntegrationRequest","type":"StripeMcpIntegrationRequest","kind":"json","required":false},{"name":"typegen_80_BraveSearchMcpIntegrationRequest","type":"BraveSearchMcpIntegrationRequest","kind":"json","required":false},{"name":"typegen_81_ObsidianMcpIntegrationRequest","type":"ObsidianMcpIntegrationRequest","kind":"json","required":false},{"name":"typegen_82_EmailTemplateDto","type":"EmailTemplateDto","kind":"json","required":false},{"name":"typegen_83_PushTemplateDto","type":"PushTemplateDto","kind":"json","required":false},{"name":"typegen_84_SmsTemplateDto","type":"SmsTemplateDto","kind":"json","required":false},{"name":"typegen_85_SystemEmailTemplateDto","type":"SystemEmailTemplateDto","kind":"json","required":false},{"name":"typegen_86_TriggerActionEmailDto","type":"TriggerActionEmailDto","kind":"json","required":false},{"name":"typegen_87_TriggerActionPushDto","type":"TriggerActionPushDto","kind":"json","required":false},{"name":"typegen_88_TriggerActionCodeDto","type":"TriggerActionCodeDto","kind":"json","required":false},{"name":"typegen_89_TriggerActionWebhookDto","type":"TriggerActionWebhookDto","kind":"json","required":false},{"name":"typegen_236_TriggerActionSmsDto","type":"TriggerActionSmsDto","kind":"json","required":false},{"name":"typegen_237_TriggerActionSseDto","type":"TriggerActionSseDto","kind":"json","required":false},{"name":"typegen_238_TriggerActionMarketplaceDto","type":"TriggerActionMarketplaceDto","kind":"json","required":false},{"name":"typegen_239_SseDeliverySettingsDto","type":"SseDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_240_GetTriggers","type":"GetTriggers","kind":"json","required":false},{"name":"typegen_241_GetTriggersResponse","type":"GetTriggersResponse","kind":"json","required":false},{"name":"typegen_90_EmailToAllUsersDeliverySettingsDto","type":"EmailToAllUsersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_91_EmailToAccountUsersDeliverySettingsDto","type":"EmailToAccountUsersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_92_EmailToUsersDeliverySettingsDto","type":"EmailToUsersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_93_EmailToEmailAddressesDeliverySettingsDto","type":"EmailToEmailAddressesDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_94_EmailToCollectionRecordsDeliverySettingsDto","type":"EmailToCollectionRecordsDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_95_PushToAllUsersDeliverySettingsDto","type":"PushToAllUsersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_96_PushToUsersDeliverySettingsDto","type":"PushToUsersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_229_PushToAccountUsersDeliverySettingsDto","type":"PushToAccountUsersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_97_PushToCollectionRecordsDeliverySettingsDto","type":"PushToCollectionRecordsDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_98_PushToDevicesDeliverySettingsDto","type":"PushToDevicesDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_99_SmsToAllUsersDeliverySettingsDto","type":"SmsToAllUsersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_100_SmsToUsersDeliverySettingsDto","type":"SmsToUsersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_101_SmsToCollectionRecordsDeliverySettingsDto","type":"SmsToCollectionRecordsDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_102_SmsToPhoneNumbersDeliverySettingsDto","type":"SmsToPhoneNumbersDeliverySettingsDto","kind":"json","required":false},{"name":"typegen_103_OpenAiLlmIntegrationDto","type":"OpenAiLlmIntegrationDto","kind":"json","required":false},{"name":"typegen_104_AnthropicLlmIntegrationDto","type":"AnthropicLlmIntegrationDto","kind":"json","required":false},{"name":"typegen_105_OllamaLlmIntegrationDto","type":"OllamaLlmIntegrationDto","kind":"json","required":false},{"name":"typegen_106_GroqLlmIntegrationDto","type":"GroqLlmIntegrationDto","kind":"json","required":false},{"name":"typegen_107_GoogleLlmIntegrationDto","type":"GoogleLlmIntegrationDto","kind":"json","required":false},{"name":"typegen_108_MistralLlmIntegrationDto","type":"MistralLlmIntegrationDto","kind":"json","required":false},{"name":"typegen_109_OpenRouterLlmIntegrationDto","type":"OpenRouterLlmIntegrationDto","kind":"json","required":false},{"name":"typegen_110_GrokLlmIntegrationDto","type":"GrokLlmIntegrationDto","kind":"json","required":false},{"name":"typegen_111_DockerMcpIntegrationDto","type":"DockerMcpIntegrationDto","kind":"json","required":false},{"name":"typegen_112_GoogleCalendarMcpIntegrationDto","type":"GoogleCalendarMcpIntegrationDto","kind":"json","required":false},{"name":"typegen_113_ObsidianMcpIntegrationDto","type":"ObsidianMcpIntegrationDto","kind":"json","required":false},{"name":"typegen_114_AwsLambdaCrossAccountRoleCodeIntegrationDto","type":"AwsLambdaCrossAccountRoleCodeIntegrationDto","kind":"json","required":false},{"name":"typegen_115_AwsLambdaIamCodeIntegrationDto","type":"AwsLambdaIamCodeIntegrationDto","kind":"json","required":false},{"name":"typegen_116_AzureFunctionsCodeIntegrationDto","type":"AzureFunctionsCodeIntegrationDto","kind":"json","required":false},{"name":"typegen_118_GoogleCloudFunctionsCodeIntegrationDto","type":"GoogleCloudFunctionsCodeIntegrationDto","kind":"json","required":false},{"name":"typegen_120_AdyenPaymentIntegrationDto","type":"AdyenPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_121_AppleInAppPaymentIntegrationDto","type":"AppleInAppPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_122_GoogleInAppPaymentIntegrationDto","type":"GoogleInAppPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_123_LemonSqueezyPaymentIntegrationDto","type":"LemonSqueezyPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_124_MolliePaymentIntegrationDto","type":"MolliePaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_125_PaddlePaymentIntegrationDto","type":"PaddlePaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_126_PayPalPaymentIntegrationDto","type":"PayPalPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_127_StripePaymentIntegrationDto","type":"StripePaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_184_ShopifyPaymentIntegrationDto","type":"ShopifyPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_185_WooCommercePaymentIntegrationDto","type":"WooCommercePaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_186_MagentoPaymentIntegrationDto","type":"MagentoPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_187_BraintreePaymentIntegrationDto","type":"BraintreePaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_188_AuthorizeNetPaymentIntegrationDto","type":"AuthorizeNetPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_189_CheckOutComPaymentIntegrationDto","type":"CheckOutComPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_190_WorldpayPaymentIntegrationDto","type":"WorldpayPaymentIntegrationDto","kind":"json","required":false},{"name":"typegen_128_AppleSignInMembershipIntegrationDto","type":"AppleSignInMembershipIntegrationDto","kind":"json","required":false},{"name":"typegen_129_GitHubMembershipIntegrationDto","type":"GitHubMembershipIntegrationDto","kind":"json","required":false},{"name":"typegen_130_GoogleMembershipIntegrationDto","type":"GoogleMembershipIntegrationDto","kind":"json","required":false},{"name":"typegen_131_MetaMembershipIntegrationDto","type":"MetaMembershipIntegrationDto","kind":"json","required":false},{"name":"typegen_132_MicrosoftMembershipIntegrationDto","type":"MicrosoftMembershipIntegrationDto","kind":"json","required":false},{"name":"typegen_133_OktaMembershipIntegrationDto","type":"OktaMembershipIntegrationDto","kind":"json","required":false},{"name":"typegen_134_XMembershipIntegrationDto","type":"XMembershipIntegrationDto","kind":"json","required":false},{"name":"typegen_135_AmqpLoggingIntegrationDto","type":"AmqpLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_136_AwsKinesisLoggingIntegrationDto","type":"AwsKinesisLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_137_AwsS3CrossAccountRoleLoggingIntegrationDto","type":"AwsS3CrossAccountRoleLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_138_AwsS3IamLoggingIntegrationDto","type":"AwsS3IamLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_139_AzureOtelLoggingIntegrationDto","type":"AzureOtelLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_140_DataDogLoggingIntegrationDto","type":"DataDogLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_141_ElasticSearchLoggingIntegrationDto","type":"ElasticSearchLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_142_InternalKafkaLoggingIntegrationDto","type":"InternalKafkaLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_143_KafkaLoggingIntegrationDto","type":"KafkaLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_144_KibanaLoggingIntegrationDto","type":"KibanaLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_145_LocalFileLoggingIntegrationDto","type":"LocalFileLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_147_MongoDbLoggingIntegrationDto","type":"MongoDbLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_148_NewRelicLoggingIntegrationDto","type":"NewRelicLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_149_PrometheusLoggingIntegrationDto","type":"PrometheusLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_150_SplunkLoggingIntegrationDto","type":"SplunkLoggingIntegrationDto","kind":"json","required":false},{"name":"typegen_153_AppleICloudFilesIntegrationDto","type":"AppleICloudFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_154_AwsS3CrossAccountRoleFilesIntegrationDto","type":"AwsS3CrossAccountRoleFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_155_AwsS3IamFilesIntegrationDto","type":"AwsS3IamFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_156_AzureBlobFilesIntegrationDto","type":"AzureBlobFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_157_DropBoxFilesIntegrationDto","type":"DropBoxFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_158_FtpFilesIntegrationDto","type":"FtpFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_159_GoogleCloudFilesIntegrationDto","type":"GoogleCloudFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_160_GoogleDriveFilesIntegrationDto","type":"GoogleDriveFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_161_LocalFilesIntegrationDto","type":"LocalFilesIntegrationDto","kind":"json","required":false},{"name":"typegen_164_MongoDbConnectionStringIntegrationDto","type":"MongoDbConnectionStringIntegrationDto","kind":"json","required":false},{"name":"typegen_165_MongoDbAtlasFlexManagedIntegrationDto","type":"MongoDbAtlasFlexManagedIntegrationDto","kind":"json","required":false},{"name":"typegen_165_BirdSmsIntegrationDto","type":"BirdSmsIntegrationDto","kind":"json","required":false},{"name":"typegen_166_PlivoSmsIntegrationDto","type":"PlivoSmsIntegrationDto","kind":"json","required":false},{"name":"typegen_167_SinchSmsIntegrationDto","type":"SinchSmsIntegrationDto","kind":"json","required":false},{"name":"typegen_168_TelesignSmsIntegrationDto","type":"TelesignSmsIntegrationDto","kind":"json","required":false},{"name":"typegen_169_TelnyxSmsIntegrationDto","type":"TelnyxSmsIntegrationDto","kind":"json","required":false},{"name":"typegen_170_TwilioSmsIntegrationDto","type":"TwilioSmsIntegrationDto","kind":"json","required":false},{"name":"typegen_171_VonageSmsIntegrationDto","type":"VonageSmsIntegrationDto","kind":"json","required":false},{"name":"typegen_246_FakeSmsIntegrationDto","type":"FakeSmsIntegrationDto","kind":"json","required":false},{"name":"typegen_172_AndroidFirebasePushIntegrationDto","type":"AndroidFirebasePushIntegrationDto","kind":"json","required":false},{"name":"typegen_173_AppleApnsPushIntegrationDto","type":"AppleApnsPushIntegrationDto","kind":"json","required":false},{"name":"typegen_174_ChromePluginPushIntegrationDto","type":"ChromePluginPushIntegrationDto","kind":"json","required":false},{"name":"typegen_175_ChromeWebPushIntegrationDto","type":"ChromeWebPushIntegrationDto","kind":"json","required":false},{"name":"typegen_176_EdgeWebPushIntegrationDto","type":"EdgeWebPushIntegrationDto","kind":"json","required":false},{"name":"typegen_177_FirefoxWebPushIntegrationDto","type":"FirefoxWebPushIntegrationDto","kind":"json","required":false},{"name":"typegen_178_SafariPushIntegrationDto","type":"SafariPushIntegrationDto","kind":"json","required":false},{"name":"typegen_247_FakePushIntegrationDto","type":"FakePushIntegrationDto","kind":"json","required":false},{"name":"typegen_179_AwsCrossAccountRoleEmailIntegrationDto","type":"AwsCrossAccountRoleEmailIntegrationDto","kind":"json","required":false},{"name":"typegen_180_AwsIamEmailIntegrationDto","type":"AwsIamEmailIntegrationDto","kind":"json","required":false},{"name":"typegen_181_MailGunEmailIntegrationDto","type":"MailGunEmailIntegrationDto","kind":"json","required":false},{"name":"typegen_182_SendGridEmailIntegrationDto","type":"SendGridEmailIntegrationDto","kind":"json","required":false},{"name":"typegen_183_SmtpEmailIntegrationDto","type":"SmtpEmailIntegrationDto","kind":"json","required":false},{"name":"typegen_248_FakeEmailIntegrationDto","type":"FakeEmailIntegrationDto","kind":"json","required":false},{"name":"typegen_192_WebhookIntegrationDto","type":"WebhookIntegrationDto","kind":"json","required":false},{"name":"typegen_193_WebhookDestinationDto","type":"WebhookDestinationDto","kind":"json","required":false},{"name":"typegen_194_SchedulerTaskDto","type":"SchedulerTaskDto","kind":"json","required":false},{"name":"typegen_195_MongoDbAggregateDto","type":"MongoDbAggregateDto","kind":"json","required":false},{"name":"typegen_196_MarketplaceIntegrationDto","type":"MarketplaceIntegrationDto","kind":"json","required":false},{"name":"typegen_197_MarketplaceFunctionDto","type":"MarketplaceFunctionDto","kind":"json","required":false},{"name":"typegen_198_MarketplaceListingDto","type":"MarketplaceListingDto","kind":"json","required":false},{"name":"typegen_199_MarketplaceFunctionDefinitionDto","type":"MarketplaceFunctionDefinitionDto","kind":"json","required":false},{"name":"typegen_200_MarketplaceFunctionParameterDto","type":"MarketplaceFunctionParameterDto","kind":"json","required":false},{"name":"typegen_201_EnableCode","type":"EnableCode","kind":"json","required":false},{"name":"typegen_202_DisableCode","type":"DisableCode","kind":"json","required":false},{"name":"typegen_203_GetCodeIntegrations","type":"GetCodeIntegrations","kind":"json","required":false},{"name":"typegen_204_GetCodeIntegration","type":"GetCodeIntegration","kind":"json","required":false},{"name":"typegen_205_SaveCodeIntegration","type":"SaveCodeIntegration","kind":"json","required":false},{"name":"typegen_206_TestCodeIntegration","type":"TestCodeIntegration","kind":"json","required":false},{"name":"typegen_207_ConfirmCodeIntegrationHumanDeliveryRequest","type":"ConfirmCodeIntegrationHumanDeliveryRequest","kind":"json","required":false},{"name":"typegen_208_SetCodeIntegrationAsDefault","type":"SetCodeIntegrationAsDefault","kind":"json","required":false},{"name":"typegen_209_DeleteCodeIntegrationRequest","type":"DeleteCodeIntegrationRequest","kind":"json","required":false},{"name":"typegen_210_EnableCodeIntegrationRequest","type":"EnableCodeIntegrationRequest","kind":"json","required":false},{"name":"typegen_211_DisableCodeIntegrationRequest","type":"DisableCodeIntegrationRequest","kind":"json","required":false},{"name":"typegen_212_GetMarketplaceListings","type":"GetMarketplaceListings","kind":"json","required":false},{"name":"typegen_213_GetMarketplaceListingFunctionTokens","type":"GetMarketplaceListingFunctionTokens","kind":"json","required":false},{"name":"typegen_214_GetMarketplaceIntegrations","type":"GetMarketplaceIntegrations","kind":"json","required":false},{"name":"typegen_215_GetMarketplaceIntegration","type":"GetMarketplaceIntegration","kind":"json","required":false},{"name":"typegen_216_SaveMarketplaceIntegration","type":"SaveMarketplaceIntegration","kind":"json","required":false},{"name":"typegen_217_DeleteMarketplaceIntegration","type":"DeleteMarketplaceIntegration","kind":"json","required":false},{"name":"typegen_218_EnableMarketplaceIntegration","type":"EnableMarketplaceIntegration","kind":"json","required":false},{"name":"typegen_219_DisableMarketplaceIntegration","type":"DisableMarketplaceIntegration","kind":"json","required":false},{"name":"typegen_221_GetMarketplaceFunctions","type":"GetMarketplaceFunctions","kind":"json","required":false},{"name":"typegen_222_GetMarketplaceFunction","type":"GetMarketplaceFunction","kind":"json","required":false},{"name":"typegen_223_SaveMarketplaceFunction","type":"SaveMarketplaceFunction","kind":"json","required":false},{"name":"typegen_224_DeleteMarketplaceFunction","type":"DeleteMarketplaceFunction","kind":"json","required":false},{"name":"typegen_225_EnableMarketplaceFunction","type":"EnableMarketplaceFunction","kind":"json","required":false},{"name":"typegen_226_DisableMarketplaceFunction","type":"DisableMarketplaceFunction","kind":"json","required":false},{"name":"typegen_227_GetMarketplaceFunctionTokens","type":"GetMarketplaceFunctionTokens","kind":"json","required":false},{"name":"typegen_228_InvokeMarketplaceFunction","type":"InvokeMarketplaceFunction","kind":"json","required":false},{"name":"typegen_232_GetMarketplaceListing","type":"GetMarketplaceListing","kind":"json","required":false},{"name":"typegen_233_TestMarketplaceIntegration","type":"TestMarketplaceIntegration","kind":"json","required":false},{"name":"typegen_234_TestMarketplaceIntegrationResponse","type":"TestMarketplaceIntegrationResponse","kind":"json","required":false},{"name":"typegen_235_GetMarketplaceListingResponse","type":"GetMarketplaceListingResponse","kind":"json","required":false},{"name":"typegen_230_AdminPortalStructureDto","type":"AdminPortalStructureDto","kind":"json","required":false},{"name":"typegen_231_AdminPortalModuleDto","type":"AdminPortalModuleDto","kind":"json","required":false},{"name":"typegen_236_UserMessageEntryWireDto","type":"UserMessageEntryWireDto","kind":"json","required":false},{"name":"typegen_237_AssistantTextEntryWireDto","type":"AssistantTextEntryWireDto","kind":"json","required":false},{"name":"typegen_238_AssistantQuestionEntryWireDto","type":"AssistantQuestionEntryWireDto","kind":"json","required":false},{"name":"typegen_239_UserAnswerEntryWireDto","type":"UserAnswerEntryWireDto","kind":"json","required":false},{"name":"typegen_240_PlanEntryWireDto","type":"PlanEntryWireDto","kind":"json","required":false},{"name":"typegen_241_UserDecisionEntryWireDto","type":"UserDecisionEntryWireDto","kind":"json","required":false},{"name":"typegen_242_RunStepEntryWireDto","type":"RunStepEntryWireDto","kind":"json","required":false},{"name":"typegen_243_ActionPendingEntryWireDto","type":"ActionPendingEntryWireDto","kind":"json","required":false},{"name":"typegen_244_NoticeEntryWireDto","type":"NoticeEntryWireDto","kind":"json","required":false},{"name":"typegen_245_ConversationSnapshotEntryWireDto","type":"ConversationSnapshotEntryWireDto","kind":"json","required":false}]}},"licensing":{"startLicenseDomainVerification":{"http":"POST","path":"/{version}/licensing/domain-verification/start","dto":"StartLicenseDomainVerificationRequest","fields":[{"name":"domain","type":"string","kind":"string","required":false}]},"getLicenseDomainVerificationStatus":{"http":"GET","path":"/{version}/licensing/domain-verification/status","dto":"GetLicenseDomainVerificationStatus","fields":[{"name":"domain","type":"string","kind":"string","required":false}]},"postLicenseHeartbeat":{"http":"POST","path":"/{version}/licensing/heartbeat","dto":"PostLicenseHeartbeat","fields":[{"name":"license","type":"string","kind":"string","required":false},{"name":"licenseAccountId","type":"string","kind":"string","required":false},{"name":"installationId","type":"string","kind":"string","required":false},{"name":"domain","type":"string","kind":"string","required":false},{"name":"hostKind","type":"string","kind":"string","required":false},{"name":"release","type":"string","kind":"string","required":false},{"name":"instanceVersion","type":"string","kind":"string","required":false}]}},"logs":{"disableLogging":{"http":"GET","path":"/{version}/logs/disable","dto":"DisableLogging","fields":[]},"enableLogging":{"http":"GET","path":"/{version}/logs/enable","dto":"EnableLogging","fields":[{"name":"createNorbixLogging","type":"boolean","kind":"boolean","required":true}]},"deleteLoggingIntegration":{"http":"DELETE","path":"/{version}/logs/integrations/{Id}","dto":"DeleteLoggingIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"wipeLogs","type":"boolean","kind":"boolean","required":true}]},"disableLoggingIntegration":{"http":"PUT","path":"/{version}/logs/integrations/{Id}/disable","dto":"DisableLoggingIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableLoggingIntegration":{"http":"PUT","path":"/{version}/logs/integrations/{Id}/enable","dto":"EnableLoggingIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getLoggingIntegration":{"http":"GET","path":"/{version}/logs/integrations/{id}","dto":"GetLoggingIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getLoggingIntegrations":{"http":"GET","path":"/{version}/logs/integrations","dto":"GetLoggingIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveLoggingIntegration":{"http":"POST","path":"/{version}/logs/integrations","dto":"SaveLoggingIntegration","fields":[{"name":"integration","type":"LoggingIntegrationRequest","kind":"json","required":true}]},"testLoggingIntegration":{"http":"POST","path":"/{version}/logs/integrations/test","dto":"TestLoggingIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"cleanLogs":{"http":"POST","path":"/{version}/logs/clean","dto":"CleanLogs","fields":[]},"getLogsByCorrelationId":{"http":"GET","path":"/{version}/logs/audit","dto":"GetLogsByCorrelationId","fields":[{"name":"targetCorrelationId","type":"string","kind":"string","required":true}]},"getLogs":{"http":"GET","path":"/{version}/logs","dto":"GetLogs","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"level","type":"string","kind":"string","required":false},{"name":"module","type":"string","kind":"string","required":false},{"name":"logCorrelationId","type":"string","kind":"string","required":false},{"name":"eventCode","type":"string","kind":"string","required":false},{"name":"search","type":"string","kind":"string","required":false},{"name":"fromUtc","type":"string","kind":"string","required":false},{"name":"toUtc","type":"string","kind":"string","required":false}]},"getLogSettings":{"http":"GET","path":"/{version}/logs/settings","dto":"GetLogSettings","fields":[]},"saveLogSettings":{"http":"POST","path":"/{version}/logs/settings","dto":"SaveLogSettings","fields":[{"name":"skipCloudDashboardLogs","type":"boolean","kind":"boolean","required":true},{"name":"skipHttpBodyMeta","type":"boolean","kind":"boolean","required":true},{"name":"aiChatLoggingEnabled","type":"boolean","kind":"boolean","required":true}]}},"membership":{"disableMembership":{"http":"GET","path":"/{version}/membership/disable","dto":"DisableMembership","fields":[]},"enableMembership":{"http":"GET","path":"/{version}/membership/enable","dto":"EnableMembership","fields":[]},"issueServiceUserApiKey":{"http":"POST","path":"/{version}/membership/users/{Id}/api-keys","dto":"IssueServiceUserApiKeyRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"name","type":"string","kind":"string","required":true},{"name":"scopes","type":"string[]","kind":"string[]","required":false},{"name":"expiresInDays","type":"number","kind":"number","required":false},{"name":"notes","type":"string","kind":"string","required":false}]},"listServiceUserApiKeys":{"http":"GET","path":"/{version}/membership/users/{Id}/api-keys","dto":"ListServiceUserApiKeysRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"deleteServiceUserApiKey":{"http":"DELETE","path":"/{version}/membership/users/{Id}/api-keys/{KeyId}","dto":"DeleteServiceUserApiKeyRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"keyId","type":"number","kind":"number","required":true}]},"deleteMembershipTrigger":{"http":"DELETE","path":"/{version}/membership/triggers/{triggerId}","dto":"DeleteMembershipTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"disableMembershipTrigger":{"http":"PATCH","path":"/{version}/membership/triggers/{triggerId}/disable","dto":"DisableMembershipTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"enableMembershipTrigger":{"http":"PATCH","path":"/{version}/membership/triggers/{triggerId}/enable","dto":"EnableMembershipTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"getMembershipTrigger":{"http":"GET","path":"/{version}/membership/triggers/{id}","dto":"GetMembershipTrigger","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"schemaId","type":"string","kind":"string","required":false}]},"getMembershipTriggers":{"http":"GET","path":"/{version}/membership/triggers","dto":"GetMembershipTriggers","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"schemaId","type":"string","kind":"string","required":false}]},"saveMembershipTrigger":{"http":"POST","path":"/{version}/membership/triggers","dto":"SaveMembershipTrigger","fields":[{"name":"trigger","type":"SaveTriggerRequest","kind":"json","required":true}]},"createRole":{"http":"POST","path":"/{version}/membership/roles","dto":"CreateRole","fields":[{"name":"roleName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"policies","type":"string[]","kind":"string[]","required":false}]},"deleteRole":{"http":"DELETE","path":"/{version}/membership/roles","dto":"DeleteRole","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getRole":{"http":"GET","path":"/{version}/membership/roles/{Id}","dto":"GetRole","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getRoles":{"http":"GET","path":"/{version}/membership/roles","dto":"GetRoles","fields":[]},"updateRolePolicies":{"http":"PATCH","path":"/{version}/membership/roles","dto":"UpdateRolePolicies","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"roleName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"policies","type":"string[]","kind":"string[]","required":false}]},"createPolicy":{"http":"POST","path":"/{version}/membership/policies","dto":"CreatePolicy","fields":[{"name":"policyName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"policyDocumentJson","type":"string","kind":"string","required":false}]},"deletePolicy":{"http":"DELETE","path":"/{version}/membership/policies","dto":"DeletePolicy","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getPolicy":{"http":"GET","path":"/{version}/membership/policies/{Id}","dto":"GetPolicy","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getPolicies":{"http":"GET","path":"/{version}/membership/policies","dto":"GetPolicies","fields":[]},"updatePolicy":{"http":"PUT","path":"/{version}/membership/policies","dto":"UpdatePolicy","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"policyName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"policyDocumentJson","type":"string","kind":"string","required":false}]},"getPasskeySettings":{"http":"GET","path":"/{version}/membership/passkey/settings","dto":"GetPasskeySettings","fields":[]},"savePasskeySettings":{"http":"POST","path":"/{version}/membership/passkey/settings","dto":"SavePasskeySettings","fields":[{"name":"enabled","type":"boolean","kind":"boolean","required":true},{"name":"codeTtlMinutes","type":"number","kind":"number","required":true},{"name":"maxCredentialsPerUser","type":"number","kind":"number","required":true},{"name":"recoveryCodeCount","type":"number","kind":"number","required":true},{"name":"generateRecoveryCodesAtSignup","type":"boolean","kind":"boolean","required":true},{"name":"authenticatorAttachment","type":"string","kind":"string","required":true},{"name":"allowMagicLinkRecovery","type":"boolean","kind":"boolean","required":true},{"name":"refreshTokenTtlDays","type":"number","kind":"number","required":true},{"name":"rpId","type":"string","kind":"string","required":false}]},"deleteMembershipIntegration":{"http":"DELETE","path":"/{version}/membership/integrations/{Id}","dto":"DeleteMembershipIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableMembershipIntegration":{"http":"PUT","path":"/{version}/membership/integrations/{Id}/disable","dto":"DisableMembershipIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableMembershipIntegration":{"http":"PUT","path":"/{version}/membership/integrations/{Id}/enable","dto":"EnableMembershipIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getMembershipIntegration":{"http":"GET","path":"/{version}/membership/integrations/{id}","dto":"GetMembershipIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getMembershipIntegrations":{"http":"GET","path":"/{version}/membership/integrations","dto":"GetMembershipIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveMembershipIntegration":{"http":"POST","path":"/{version}/membership/integrations","dto":"SaveMembershipIntegration","fields":[{"name":"integration","type":"MembershipIntegrationRequest","kind":"json","required":true}]},"setMembershipIntegrationAsDefault":{"http":"PUT","path":"/{version}/membership/integrations/{Id}/default","dto":"SetMembershipIntegrationAsDefaultRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getAuthorizationSettings":{"http":"GET","path":"/{version}/membership/authorization","dto":"GetAuthorizationSettings","fields":[]},"updateAuthorizationSettings":{"http":"PUT","path":"/{version}/membership/authorization","dto":"UpdateAuthorizationSettings","fields":[{"name":"setting","type":"string","kind":"string","required":false},{"name":"defaultRoles","type":"string[]","kind":"string[]","required":false},{"name":"allowedRegistrationRoles","type":"string[]","kind":"string[]","required":false},{"name":"allowGuestUsers","type":"boolean","kind":"boolean","required":true},{"name":"guestCleanupPeriodDays","type":"number","kind":"number","required":false},{"name":"userRegistersAsRole","type":"string","kind":"string","required":false},{"name":"guestRegistersAsRole","type":"string","kind":"string","required":false},{"name":"allowedRegisterRoles","type":"string[]","kind":"string[]","required":false},{"name":"needVerification","type":"boolean","kind":"boolean","required":true},{"name":"verificationEmailTemplate","type":"string","kind":"string","required":false},{"name":"deactivationEmailTemplate","type":"string","kind":"string","required":false},{"name":"allowInviteUsers","type":"boolean","kind":"boolean","required":true},{"name":"allowDeactivateUsers","type":"boolean","kind":"boolean","required":true},{"name":"inviteUserEmailTemplate","type":"string","kind":"string","required":false},{"name":"invitationExpiration","type":"number","kind":"number","required":false},{"name":"emailVerificationExpiration","type":"number","kind":"number","required":false},{"name":"deactivationExpiration","type":"number","kind":"number","required":false},{"name":"defaultSubscribeToNews","type":"boolean","kind":"boolean","required":true},{"name":"minLength","type":"number","kind":"number","required":true},{"name":"maxLength","type":"number","kind":"number","required":false},{"name":"minNumbers","type":"number","kind":"number","required":false},{"name":"maxNumbers","type":"number","kind":"number","required":false},{"name":"minUpper","type":"number","kind":"number","required":false},{"name":"maxUpper","type":"number","kind":"number","required":false},{"name":"minLower","type":"number","kind":"number","required":false},{"name":"maxLower","type":"number","kind":"number","required":false},{"name":"minSpecial","type":"number","kind":"number","required":false},{"name":"maxSpecial","type":"number","kind":"number","required":false},{"name":"allowedSpecial","type":"string","kind":"string","required":false}]},"updatePasswordComplexity":{"http":"PUT","path":"/{version}/membership/authorization/password-complexity","dto":"UpdatePasswordComplexity","fields":[{"name":"minLength","type":"number","kind":"number","required":true},{"name":"maxLength","type":"number","kind":"number","required":false},{"name":"minNumbers","type":"number","kind":"number","required":false},{"name":"maxNumbers","type":"number","kind":"number","required":false},{"name":"minUpper","type":"number","kind":"number","required":false},{"name":"maxUpper","type":"number","kind":"number","required":false},{"name":"minLower","type":"number","kind":"number","required":false},{"name":"maxLower","type":"number","kind":"number","required":false},{"name":"minSpecial","type":"number","kind":"number","required":false},{"name":"maxSpecial","type":"number","kind":"number","required":false},{"name":"allowedSpecial","type":"string","kind":"string","required":false}]},"getAuthenticationSettings":{"http":"GET","path":"/{version}/membership/authentication","dto":"GetAuthenticationSettings","fields":[]},"updateAuthenticationSettings":{"http":"PUT","path":"/{version}/membership/authentication","dto":"UpdateAuthenticationSettings","fields":[{"name":"logoutUrl","type":"string","kind":"string","required":false},{"name":"allowUsernames","type":"boolean","kind":"boolean","required":true},{"name":"modes","type":"CredentialsSettingsModeDto[]","kind":"json[]","required":false}]},"createContact":{"http":"POST","path":"/{version}/membership/users","dto":"CreateContactRequest","fields":[{"name":"primaryEmail","type":"string","kind":"string","required":false},{"name":"primaryPhone","type":"string","kind":"string","required":false},{"name":"displayName","type":"string","kind":"string","required":false},{"name":"firstName","type":"string","kind":"string","required":false},{"name":"lastName","type":"string","kind":"string","required":false}]},"deleteContact":{"http":"DELETE","path":"/{version}/membership/users/{contactId}","dto":"DeleteContact","fields":[{"name":"contactId","type":"string","kind":"string","required":true}]},"getContact":{"http":"GET","path":"/{version}/membership/users/{contactId}","dto":"GetContact","fields":[{"name":"contactId","type":"string","kind":"string","required":true}]},"getAllContacts":{"http":"GET","path":"/{version}/membership/users","dto":"GetAllContacts","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false}]},"mergeContacts":{"http":"POST","path":"/{version}/membership/users/merge","dto":"MergeContactsRequest","fields":[{"name":"survivorId","type":"string","kind":"string","required":true},{"name":"mergedIds","type":"string[]","kind":"string[]","required":true}]},"updateContact":{"http":"PATCH","path":"/{version}/membership/users/{contactId}","dto":"UpdateContactRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"displayName","type":"string","kind":"string","required":false},{"name":"firstName","type":"string","kind":"string","required":false},{"name":"lastName","type":"string","kind":"string","required":false},{"name":"fullName","type":"string","kind":"string","required":false},{"name":"company","type":"string","kind":"string","required":false},{"name":"notes","type":"string","kind":"string","required":false},{"name":"gender","type":"string","kind":"string","required":false},{"name":"birthDate","type":"number","kind":"number","required":false},{"name":"timeZone","type":"string","kind":"string","required":false},{"name":"language","type":"string","kind":"string","required":false},{"name":"addressLine1","type":"string","kind":"string","required":false},{"name":"addressLine2","type":"string","kind":"string","required":false},{"name":"country","type":"string","kind":"string","required":false},{"name":"city","type":"string","kind":"string","required":false},{"name":"state","type":"string","kind":"string","required":false},{"name":"postalCode","type":"string","kind":"string","required":false}]},"addContactIdentity":{"http":"POST","path":"/{version}/membership/users/{contactId}/identities","dto":"AddContactIdentityRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"authId","type":"string","kind":"string","required":true}]},"promoteContactIdentity":{"http":"POST","path":"/{version}/membership/users/{contactId}/identities/{authId}/promote","dto":"PromoteContactIdentityRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"authId","type":"string","kind":"string","required":true}]},"removeContactIdentity":{"http":"DELETE","path":"/{version}/membership/users/{contactId}/identities/{authId}","dto":"RemoveContactIdentityRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"authId","type":"string","kind":"string","required":true}]}},"notifications":{"disableEmail":{"http":"GET","path":"/{version}/notifications/email/disable","dto":"DisableEmail","fields":[]},"getEmailDisableDependencies":{"http":"GET","path":"/{version}/notifications/email/disable-dependencies","dto":"GetEmailDisableDependencies","fields":[]},"enableEmail":{"http":"GET","path":"/{version}/notifications/email/enable","dto":"EnableEmail","fields":[]},"saveEmailValidationIntegration":{"http":"POST","path":"/{version}/notifications/email/validation/integrations","dto":"SaveEmailValidationIntegration","fields":[{"name":"integration","type":"EmailValidationIntegrationRequest","kind":"json","required":true}]},"testEmailValidationIntegration":{"http":"POST","path":"/{version}/notifications/email/validation/integrations/test","dto":"TestEmailValidationIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"attachFileToTemplate":{"http":"POST","path":"/{version}/notifications/email/templates/attachments","dto":"AttachFileToTemplateRequest","fields":[{"name":"language","type":"string","kind":"string","required":false},{"name":"templateId","type":"string","kind":"string","required":true},{"name":"fileRef","type":"FileResourceRefDto","kind":"json","required":true}]},"createEmailTemplate":{"http":"POST","path":"/{version}/notifications/email/templates","dto":"CreateEmailTemplateRequest","fields":[{"name":"templateName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"communicationChannel","type":"CommunicationChannel","kind":"string","required":true,"options":["Transactional","Marketing","System"]},{"name":"tags","type":"string[]","kind":"string[]","required":false},{"name":"staticAttachments","type":"FileResourceRefDto[]","kind":"json[]","required":false},{"name":"translations","type":"EmailMessageTranslationDto[]","kind":"json[]","required":true}]},"deleteEmailTemplate":{"http":"DELETE","path":"/{version}/notifications/email/templates/{Id}","dto":"DeleteEmailTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getEmailTemplate":{"http":"GET","path":"/{version}/notifications/email/templates/{id}","dto":"GetEmailTemplate","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getEmailTemplates":{"http":"GET","path":"/{version}/notifications/email/templates","dto":"GetEmailTemplates","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"showArchived","type":"boolean","kind":"boolean","required":false},{"name":"templateId","type":"string","kind":"string","required":false}]},"getMjml":{"http":"POST","path":"/{version}/notifications/email/templates/mjml","dto":"GetMjml","fields":[{"name":"code","type":"string","kind":"string","required":true},{"name":"tokens","type":"TokenMappingDto[]","kind":"json[]","required":false},{"name":"isForPreview","type":"boolean","kind":"boolean","required":true}]},"getSystemEmailTemplate":{"http":"GET","path":"/{version}/notifications/email/system-templates/{id}","dto":"GetSystemEmailTemplate","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getSystemEmailTemplates":{"http":"GET","path":"/{version}/notifications/email/system-templates","dto":"GetSystemEmailTemplates","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"groupTags","type":"string[]","kind":"string[]","required":false},{"name":"themes","type":"string[]","kind":"string[]","required":false},{"name":"communicationChannel","type":"CommunicationChannel","kind":"string","required":false,"options":["Transactional","Marketing","System"]},{"name":"forTrigger","type":"TriggerType","kind":"string","required":false,"options":["Membership","Schema","Files","Payments"]}]},"getEmailTemplateAvailableTokens":{"http":"GET","path":"/{version}/notifications/email/templates/{id}/tokens","dto":"GetEmailTemplateAvailableTokens","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"updateEmailTemplate":{"http":"PUT","path":"/{version}/notifications/email/templates","dto":"UpdateEmailTemplateRequest","fields":[{"name":"templateName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"communicationChannel","type":"CommunicationChannel","kind":"string","required":true,"options":["Transactional","Marketing","System"]},{"name":"tags","type":"string[]","kind":"string[]","required":false},{"name":"staticAttachments","type":"FileResourceRefDto[]","kind":"json[]","required":false},{"name":"translations","type":"EmailMessageTranslationDto[]","kind":"json[]","required":true},{"name":"viewId","type":"string","kind":"string","required":true}]},"deleteEmailSignature":{"http":"DELETE","path":"/{version}/notifications/email/signatures/{id}","dto":"DeleteEmailSignature","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getEmailSignature":{"http":"GET","path":"/{version}/notifications/email/signatures/{id}","dto":"GetEmailSignature","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getEmailSignatures":{"http":"GET","path":"/{version}/notifications/email/signatures","dto":"GetEmailSignatures","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveEmailSignature":{"http":"POST","path":"/{version}/notifications/email/signatures","dto":"SaveEmailSignatureRequest","fields":[{"name":"viewId","type":"string","kind":"string","required":false},{"name":"displayName","type":"string","kind":"string","required":true},{"name":"translations","type":"TranslationDto[]","kind":"json[]","required":true}]},"getEmailSettings":{"http":"GET","path":"/{version}/notifications/email/settings","dto":"GetEmailSettings","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"confirmEmailIntegrationHumanDelivery":{"http":"POST","path":"/{version}/notifications/email/integrations/confirm-human-delivery","dto":"ConfirmEmailIntegrationHumanDeliveryRequest","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"deleteEmailIntegration":{"http":"DELETE","path":"/{version}/notifications/email/integrations/{Id}","dto":"DeleteEmailIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableEmailIntegration":{"http":"PUT","path":"/{version}/notifications/email/integrations/{Id}/disable","dto":"DisableEmailIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"checkEmailIntegrationDomainHealth":{"http":"POST","path":"/{version}/notifications/email/integrations/domain-health","dto":"CheckEmailIntegrationDomainHealthRequest","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"enableEmailIntegration":{"http":"PUT","path":"/{version}/notifications/email/integrations/{Id}/enable","dto":"EnableEmailIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getEmailIntegration":{"http":"GET","path":"/{version}/notifications/email/integrations/{id}","dto":"GetEmailIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getEmailIntegrations":{"http":"GET","path":"/{version}/notifications/email/integrations","dto":"GetEmailIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveEmailIntegration":{"http":"POST","path":"/{version}/notifications/email/integrations","dto":"SaveEmailIntegration","fields":[{"name":"integration","type":"EmailIntegrationRequest","kind":"json","required":true}]},"setEmailsIntegrationAsDefault":{"http":"PUT","path":"/{version}/notifications/email/integrations/{Id}/default","dto":"SetEmailsIntegrationAsDefault","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"testEmailIntegration":{"http":"POST","path":"/{version}/notifications/email/integrations/test","dto":"TestEmailIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true},{"name":"to","type":"string","kind":"string","required":true}]},"archiveEmailTemplate":{"http":"PUT","path":"/{version}/notifications/email/templates/{Id}/archive","dto":"ArchiveEmailTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"cloneEmailTemplate":{"http":"POST","path":"/{version}/notifications/email/templates/{Id}/clone","dto":"CloneEmailTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"unArchiveEmailTemplate":{"http":"PUT","path":"/{version}/notifications/email/templates/{Id}/unarchive","dto":"UnArchiveEmailTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"deleteEmailFooter":{"http":"DELETE","path":"/{version}/notifications/email/footers/{id}","dto":"DeleteEmailFooter","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getEmailFooter":{"http":"GET","path":"/{version}/notifications/email/footers/{id}","dto":"GetEmailFooter","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getEmailFooters":{"http":"GET","path":"/{version}/notifications/email/footers","dto":"GetEmailFooters","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveEmailFooter":{"http":"POST","path":"/{version}/notifications/email/footers","dto":"SaveEmailFooterRequest","fields":[{"name":"viewId","type":"string","kind":"string","required":false},{"name":"displayName","type":"string","kind":"string","required":true},{"name":"translations","type":"TranslationDto[]","kind":"json[]","required":true}]},"createEmailCampaign":{"http":"POST","path":"/{version}/notifications/email/campaigns","dto":"CreateEmailCampaignRequest","fields":[{"name":"campaign","type":"EmailCampaignRequest","kind":"json","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"deleteEmailCampaign":{"http":"DELETE","path":"/{version}/notifications/email/campaigns/{Id}","dto":"DeleteEmailCampaignRequest","fields":[]},"getEmailCampaign":{"http":"GET","path":"/{version}/notifications/email/campaigns/{id}","dto":"GetEmailCampaign","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getEmailCampaigns":{"http":"GET","path":"/{version}/notifications/email/campaigns","dto":"GetEmailCampaigns","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"campaignId","type":"string","kind":"string","required":false},{"name":"emailAddress","type":"string","kind":"string","required":false},{"name":"templateId","type":"string","kind":"string","required":false},{"name":"from","type":"number","kind":"number","required":false},{"name":"to","type":"number","kind":"number","required":false}]},"getEmailCampaignBatches":{"http":"GET","path":"/{version}/notifications/email/campaigns/{id}/batches","dto":"GetEmailCampaignBatches","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"batchId","type":"string","kind":"string","required":false},{"name":"emailAddress","type":"string","kind":"string","required":false}]},"getEmailCampaignBatchNotification":{"http":"GET","path":"/{version}/notifications/email/campaigns/{id}/batches/{batchId}/{notificationId}","dto":"GetEmailCampaignBatchNotification","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":true},{"name":"batchId","type":"string","kind":"string","required":true},{"name":"notificationId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getEmailCampaignBatchNotifications":{"http":"GET","path":"/{version}/notifications/email/campaigns/{id}/batches/{batchId}","dto":"GetEmailCampaignBatchNotifications","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":true},{"name":"batchId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getEmailCampaignStatistics":{"http":"GET","path":"/{version}/notifications/email/campaigns/{id}/stats","dto":"GetEmailCampaignStatistics","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"previewEmailNotification":{"http":"GET","path":"/{version}/notifications/email/preview","dto":"PreviewEmailNotification","fields":[{"name":"hash","type":"string","kind":"string","required":true}]},"stopEmailCampaign":{"http":"POST","path":"/{version}/notifications/email/campaigns/{Id}/stop","dto":"StopEmailCampaignRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getEmailCampaignMessage":{"http":"GET","path":"/{version}/notifications/emails/campaigns/{campaignId}/messages/{notificationId}","dto":"GetEmailCampaignMessage","fields":[{"name":"campaignId","type":"string","kind":"string","required":true},{"name":"campaignBatchId","type":"string","kind":"string","required":true},{"name":"notificationId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getEmailCampaignMessages":{"http":"GET","path":"/{version}/notifications/emails/campaigns/{campaignId}/messages","dto":"GetEmailCampaignMessagesRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"campaignId","type":"string","kind":"string","required":true},{"name":"campaignBatchId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"disableSms":{"http":"GET","path":"/{version}/notifications/sms/disable","dto":"DisableSms","fields":[]},"getSmsDisableDependencies":{"http":"GET","path":"/{version}/notifications/sms/disable-dependencies","dto":"GetSmsDisableDependencies","fields":[]},"enableSms":{"http":"GET","path":"/{version}/notifications/sms/enable","dto":"EnableSms","fields":[]},"archiveSmsTemplate":{"http":"PUT","path":"/{version}/notifications/sms/templates/{Id}/archive","dto":"ArchiveSmsTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"cloneSmsTemplate":{"http":"POST","path":"/{version}/notifications/sms/templates/{Id}/clone","dto":"CloneSmsTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"createSmsTemplate":{"http":"POST","path":"/{version}/notifications/sms/templates","dto":"CreateSmsTemplateRequest","fields":[{"name":"templateName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"communicationChannel","type":"CommunicationChannel","kind":"string","required":true,"options":["Transactional","Marketing","System"]},{"name":"tags","type":"string[]","kind":"string[]","required":false},{"name":"translations","type":"SmsMessageTranslationDto[]","kind":"json[]","required":true}]},"deleteSmsTemplate":{"http":"DELETE","path":"/{version}/notifications/sms/templates/{Id}","dto":"DeleteSmsTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getSmsTemplate":{"http":"GET","path":"/{version}/notifications/sms/templates/{id}","dto":"GetSmsTemplate","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getSmsTemplates":{"http":"GET","path":"/{version}/notifications/sms/templates","dto":"GetSmsTemplates","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"showArchived","type":"boolean","kind":"boolean","required":false},{"name":"templateId","type":"string","kind":"string","required":false}]},"getSmsMessageContentTokens":{"http":"GET","path":"/{version}/notifications/sms/templates/{id}/tokens","dto":"GetSmsMessageContentTokens","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"renderSms":{"http":"POST","path":"/{version}/notifications/sms/templates/render","dto":"RenderSms","fields":[{"name":"code","type":"string","kind":"string","required":true},{"name":"tokens","type":"TokenMappingDto[]","kind":"json[]","required":false},{"name":"isForPreview","type":"boolean","kind":"boolean","required":true}]},"unArchiveSmsTemplate":{"http":"PUT","path":"/{version}/notifications/sms/templates/{Id}/unarchive","dto":"UnArchiveSmsTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"updateSmsTemplate":{"http":"PUT","path":"/{version}/notifications/sms/templates","dto":"UpdateSmsTemplateRequest","fields":[{"name":"templateName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"communicationChannel","type":"CommunicationChannel","kind":"string","required":true,"options":["Transactional","Marketing","System"]},{"name":"tags","type":"string[]","kind":"string[]","required":false},{"name":"translations","type":"SmsMessageTranslationDto[]","kind":"json[]","required":true},{"name":"viewId","type":"string","kind":"string","required":true}]},"getSmsSettings":{"http":"GET","path":"/{version}/notifications/sms/settings","dto":"GetSmsSettings","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"confirmSmsIntegrationHumanDelivery":{"http":"POST","path":"/{version}/notifications/sms/integrations/confirm-human-delivery","dto":"ConfirmSmsIntegrationHumanDeliveryRequest","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"deleteSmsIntegration":{"http":"DELETE","path":"/{version}/notifications/sms/integrations/{Id}","dto":"DeleteSmsIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableSmsIntegration":{"http":"PUT","path":"/{version}/notifications/sms/integrations/{Id}/disable","dto":"DisableSmsIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableSmsIntegration":{"http":"PUT","path":"/{version}/notifications/sms/integrations/{Id}/enable","dto":"EnableSmsIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getSmsIntegration":{"http":"GET","path":"/{version}/notifications/sms/integrations/{id}","dto":"GetSmsIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getSmsIntegrations":{"http":"GET","path":"/{version}/notifications/sms/integrations","dto":"GetSmsIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"saveSmsIntegration":{"http":"POST","path":"/{version}/notifications/sms/integrations","dto":"SaveSmsIntegration","fields":[{"name":"integration","type":"SmsIntegrationRequest","kind":"json","required":true}]},"setSmsIntegrationAsDefault":{"http":"PUT","path":"/{version}/notifications/sms/integrations/{Id}/default","dto":"SetSmsIntegrationAsDefaultRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"testSmsIntegration":{"http":"POST","path":"/{version}/notifications/sms/integrations/test","dto":"TestSmsIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true},{"name":"to","type":"string","kind":"string","required":false}]},"createSmsCampaign":{"http":"POST","path":"/{version}/notifications/sms/campaigns","dto":"CreateSmsCampaignRequest","fields":[{"name":"templateId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"language","type":"string","kind":"string","required":false},{"name":"initiatorId","type":"string","kind":"string","required":false},{"name":"deliveryType","type":"SmsCampaignRecipientsSourceTypes","kind":"string","required":true,"options":["AllUsers","SpecifiedUsers","AccountUsers","PhoneNumbers","Collection"]},{"name":"allUsers","type":"SmsToAllUsersDeliverySettingsDto","kind":"json","required":false},{"name":"specifiedUsers","type":"SmsToUsersDeliverySettingsDto","kind":"json","required":false},{"name":"collection","type":"SmsToCollectionRecordsDeliverySettingsDto","kind":"json","required":false},{"name":"phoneNumbers","type":"SmsToPhoneNumbersDeliverySettingsDto","kind":"json","required":false}]},"deleteSmsCampaign":{"http":"DELETE","path":"/{version}/notifications/sms/campaigns/{id}","dto":"DeleteSmsCampaign","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getSmsCampaign":{"http":"GET","path":"/{version}/notifications/sms/campaigns/{id}","dto":"GetSmsCampaign","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getSmsCampaigns":{"http":"GET","path":"/{version}/notifications/sms/campaigns","dto":"GetSmsCampaigns","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"templateId","type":"string","kind":"string","required":false},{"name":"from","type":"number","kind":"number","required":false},{"name":"to","type":"number","kind":"number","required":false}]},"getSmsCampaignBatches":{"http":"GET","path":"/{version}/notifications/sms/campaigns/{id}/batches","dto":"GetSmsCampaignBatches","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getSmsCampaignBatchNotification":{"http":"GET","path":"/{version}/notifications/sms/campaigns/{id}/batches/{batchId}/{notificationId}","dto":"GetSmsCampaignBatchNotification","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":true},{"name":"batchId","type":"string","kind":"string","required":true},{"name":"notificationId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getSmsCampaignBatchNotifications":{"http":"GET","path":"/{version}/notifications/sms/campaigns/{id}/batches/{batchId}","dto":"GetSmsCampaignBatchNotifications","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":true},{"name":"batchId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getSmsCampaignStatistics":{"http":"GET","path":"/{version}/notifications/sms/campaigns/{id}/stats","dto":"GetSmsCampaignStatistics","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"previewSmsNotification":{"http":"GET","path":"/{version}/notifications/sms/preview","dto":"PreviewSmsNotification","fields":[{"name":"hash","type":"string","kind":"string","required":true}]},"stopSmsCampaign":{"http":"POST","path":"/{version}/notifications/sms/campaigns/{Id}/stop","dto":"StopSmsCampaignRequest","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getSmsCampaignMessage":{"http":"GET","path":"/{version}/notifications/sms/campaigns/{campaignId}/messages/{notificationId}","dto":"GetSmsCampaignMessage","fields":[{"name":"campaignId","type":"string","kind":"string","required":true},{"name":"campaignBatchId","type":"string","kind":"string","required":true},{"name":"notificationId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getSmsCampaignMessages":{"http":"GET","path":"/{version}/notifications/sms/campaigns/{campaignId}/messages","dto":"GetSmsCampaignMessagesRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"campaignId","type":"string","kind":"string","required":true},{"name":"campaignBatchId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"disablePush":{"http":"GET","path":"/{version}/notifications/push/disable","dto":"DisablePush","fields":[]},"getPushDisableDependencies":{"http":"GET","path":"/{version}/notifications/push/disable-dependencies","dto":"GetPushDisableDependencies","fields":[]},"enablePush":{"http":"GET","path":"/{version}/notifications/push/enable","dto":"EnablePush","fields":[]},"archivePushTemplate":{"http":"PUT","path":"/{version}/notifications/push/templates/{Id}/archive","dto":"ArchivePushTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"clonePushTemplate":{"http":"POST","path":"/{version}/notifications/push/templates/{Id}/clone","dto":"ClonePushTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"createPushTemplate":{"http":"POST","path":"/{version}/notifications/push/templates","dto":"CreatePushTemplateRequest","fields":[{"name":"templateName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"communicationChannel","type":"CommunicationChannel","kind":"string","required":true,"options":["Transactional","Marketing","System"]},{"name":"tags","type":"string[]","kind":"string[]","required":false},{"name":"translations","type":"PushMessageTranslationDto[]","kind":"json[]","required":true}]},"deletePushTemplate":{"http":"DELETE","path":"/{version}/notifications/push/templates/{Id}","dto":"DeletePushTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getPushTemplate":{"http":"GET","path":"/{version}/notifications/push/templates/{id}","dto":"GetPushTemplate","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getPushTemplates":{"http":"GET","path":"/{version}/notifications/push/templates","dto":"GetPushTemplates","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"showArchived","type":"boolean","kind":"boolean","required":false},{"name":"templateId","type":"string","kind":"string","required":false}]},"getPushMessageContentTokens":{"http":"GET","path":"/{version}/notifications/push/templates/{id}/tokens","dto":"GetPushMessageContentTokens","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"renderPush":{"http":"POST","path":"/{version}/notifications/push/templates/render","dto":"RenderPush","fields":[{"name":"code","type":"string","kind":"string","required":true},{"name":"tokens","type":"TokenMappingDto[]","kind":"json[]","required":false},{"name":"isForPreview","type":"boolean","kind":"boolean","required":true}]},"unArchivePushTemplate":{"http":"PUT","path":"/{version}/notifications/push/templates/{Id}/unarchive","dto":"UnArchivePushTemplateRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"updatePushTemplate":{"http":"PUT","path":"/{version}/notifications/push/templates","dto":"UpdatePushTemplateRequest","fields":[{"name":"templateName","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"communicationChannel","type":"CommunicationChannel","kind":"string","required":true,"options":["Transactional","Marketing","System"]},{"name":"tags","type":"string[]","kind":"string[]","required":false},{"name":"translations","type":"PushMessageTranslationDto[]","kind":"json[]","required":true},{"name":"viewId","type":"string","kind":"string","required":true}]},"getPushSettings":{"http":"GET","path":"/{version}/notifications/push/settings","dto":"GetPushSettings","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"confirmPushIntegrationHumanDelivery":{"http":"POST","path":"/{version}/notifications/push/integrations/confirm-human-delivery","dto":"ConfirmPushIntegrationHumanDeliveryRequest","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"deletePushIntegration":{"http":"DELETE","path":"/{version}/notifications/push/integrations/{Id}","dto":"DeletePushIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disablePushIntegration":{"http":"PUT","path":"/{version}/notifications/push/integrations/{Id}/disable","dto":"DisablePushIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enablePushIntegration":{"http":"PUT","path":"/{version}/notifications/push/integrations/{Id}/enable","dto":"EnablePushIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getPushIntegration":{"http":"GET","path":"/{version}/notifications/push/integrations/{id}","dto":"GetPushIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getPushIntegrations":{"http":"GET","path":"/{version}/notifications/push/integrations","dto":"GetPushIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"savePushIntegration":{"http":"POST","path":"/{version}/notifications/push/integrations","dto":"SavePushIntegration","fields":[{"name":"integration","type":"PushIntegrationRequest","kind":"json","required":true}]},"setPushIntegrationAsDefault":{"http":"PUT","path":"/{version}/notifications/push/integrations/{Id}/default","dto":"SetPushIntegrationAsDefaultRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"testPushIntegration":{"http":"POST","path":"/{version}/notifications/push/integrations/test","dto":"TestPushIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true},{"name":"testToken","type":"string","kind":"string","required":false},{"name":"deliveryFamily","type":"string","kind":"string","required":false}]},"registerDevice":{"http":"POST","path":"/{version}/notifications/push/devices","dto":"RegisterDevice","fields":[{"name":"pushDeviceDto","type":"PushDeviceDto","kind":"json","required":true},{"name":"userId","type":"string","kind":"string","required":true},{"name":"accountId","type":"string","kind":"string","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getPushDevice":{"http":"GET","path":"/{version}/notifications/push/devices/{id}","dto":"GetPushDevice","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"createPushCampaign":{"http":"POST","path":"/{version}/notifications/push/campaigns","dto":"CreatePushCampaignRequest","fields":[{"name":"campaign","type":"PushCampaignRequest","kind":"json","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"deletePushCampaign":{"http":"DELETE","path":"/{version}/notifications/push/campaigns/{Id}","dto":"DeletePushCampaignRequest","fields":[]},"getPushCampaign":{"http":"GET","path":"/{version}/notifications/push/campaigns/{id}","dto":"GetPushCampaign","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getPushCampaigns":{"http":"GET","path":"/{version}/notifications/push/campaigns","dto":"GetPushCampaigns","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"templateId","type":"string","kind":"string","required":false},{"name":"from","type":"number","kind":"number","required":false},{"name":"to","type":"number","kind":"number","required":false}]},"getPushCampaignBatches":{"http":"GET","path":"/{version}/notifications/push/campaigns/{id}/batches","dto":"GetPushCampaignBatches","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false},{"name":"batchId","type":"string","kind":"string","required":false}]},"getPushCampaignBatchNotification":{"http":"GET","path":"/{version}/notifications/push/campaigns/{id}/batches/{batchId}/{notificationId}","dto":"GetPushCampaignBatchNotification","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":true},{"name":"batchId","type":"string","kind":"string","required":true},{"name":"notificationId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getPushCampaignBatchNotifications":{"http":"GET","path":"/{version}/notifications/push/campaigns/{id}/batches/{batchId}","dto":"GetPushCampaignBatchNotifications","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"id","type":"string","kind":"string","required":true},{"name":"batchId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getPushCampaignStatistics":{"http":"GET","path":"/{version}/notifications/push/campaigns/{id}/stats","dto":"GetPushCampaignStatistics","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"previewPushNotification":{"http":"GET","path":"/{version}/notifications/push/preview","dto":"PreviewPushNotification","fields":[{"name":"hash","type":"string","kind":"string","required":true}]},"stopPushCampaign":{"http":"POST","path":"/{version}/notifications/push/campaigns/{Id}/stop","dto":"StopPushCampaignRequest","fields":[]},"getPushCampaignMessage":{"http":"GET","path":"/{version}/notifications/push/campaigns/{campaignId}/messages/{notificationId}","dto":"GetPushCampaignMessage","fields":[{"name":"campaignId","type":"string","kind":"string","required":true},{"name":"campaignBatchId","type":"string","kind":"string","required":true},{"name":"notificationId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getPushCampaignMessages":{"http":"GET","path":"/{version}/notifications/push/campaigns/{campaignId}/messages","dto":"GetPushCampaignMessagesRequest","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"campaignId","type":"string","kind":"string","required":true},{"name":"campaignBatchId","type":"string","kind":"string","required":true},{"name":"databaseIntegrationId","type":"string","kind":"string","required":false}]},"getUserNotificationPreferences":{"http":"GET","path":"/{version}/notifications/user/preferences","dto":"GetUserNotificationPreferences","fields":[{"name":"userId","type":"string","kind":"string","required":true}]},"updateUserNotificationsPreferences":{"http":"PUT","path":"/{version}/notifications/user/preferences","dto":"UpdateUserNotificationsPreferences","fields":[{"name":"userId","type":"string","kind":"string","required":true},{"name":"blockAllMarketingMessages","type":"boolean","kind":"boolean","required":true}]},"grantContactConsent":{"http":"POST","path":"/{version}/notifications/contacts/{contactId}/marketing-state/{channel}/consent","dto":"GrantContactConsentRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"channel","type":"string","kind":"string","required":true},{"name":"lawfulBasis","type":"string","kind":"string","required":true},{"name":"source","type":"string","kind":"string","required":true},{"name":"evidenceRef","type":"string","kind":"string","required":false}]},"unsubscribeContact":{"http":"POST","path":"/{version}/notifications/contacts/{contactId}/marketing-state/{channel}/unsubscribe","dto":"UnsubscribeContactRequest","fields":[{"name":"contactId","type":"string","kind":"string","required":true},{"name":"channel","type":"string","kind":"string","required":true},{"name":"reason","type":"string","kind":"string","required":false}]}},"payments":{"disablePayments":{"http":"GET","path":"/{version}/payments/disable","dto":"DisablePayments","fields":[]},"enablePayments":{"http":"GET","path":"/{version}/payments/enable","dto":"EnablePayments","fields":[]},"getPaymentsWebhookLog":{"http":"GET","path":"/{version}/payments/webhooks/log","dto":"GetPaymentsWebhookLog","fields":[{"name":"integrationId","type":"string","kind":"string","required":false},{"name":"limit","type":"number","kind":"number","required":false}]},"deletePaymentsTrigger":{"http":"DELETE","path":"/{version}/payments/triggers/{triggerId}","dto":"DeletePaymentsTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"disablePaymentsTrigger":{"http":"PATCH","path":"/{version}/payments/triggers/{triggerId}/disable","dto":"DisablePaymentsTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"enablePaymentsTrigger":{"http":"PATCH","path":"/{version}/payments/triggers/{triggerId}/enable","dto":"EnablePaymentsTrigger","fields":[{"name":"triggerId","type":"string","kind":"string","required":true},{"name":"triggerType","type":"TriggerType","kind":"string","required":true,"options":["Membership","Schema","Files","Payments"]},{"name":"schemaId","type":"string","kind":"string","required":false}]},"getPaymentsTrigger":{"http":"GET","path":"/{version}/payments/triggers/{id}","dto":"GetPaymentsTrigger","fields":[{"name":"id","type":"string","kind":"string","required":true},{"name":"schemaId","type":"string","kind":"string","required":false}]},"getPaymentsTriggers":{"http":"GET","path":"/{version}/payments/triggers","dto":"GetPaymentsTriggers","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"schemaId","type":"string","kind":"string","required":false}]},"savePaymentsTrigger":{"http":"POST","path":"/{version}/payments/triggers","dto":"SavePaymentsTrigger","fields":[{"name":"trigger","type":"SaveTriggerRequest","kind":"json","required":true}]},"confirmPaymentsIntegrationHumanDelivery":{"http":"POST","path":"/{version}/payments/integrations/confirm-human-delivery","dto":"ConfirmPaymentsIntegrationHumanDeliveryRequest","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]},"deletePaymentsIntegration":{"http":"DELETE","path":"/{version}/payments/integrations/{Id}","dto":"DeletePaymentsIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disablePaymentsIntegration":{"http":"PUT","path":"/{version}/payments/integrations/{Id}/disable","dto":"DisablePaymentsIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enablePaymentsIntegration":{"http":"PUT","path":"/{version}/payments/integrations/{Id}/enable","dto":"EnablePaymentsIntegrationRequest","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getPaymentsIntegration":{"http":"GET","path":"/{version}/payments/integrations/{id}","dto":"GetPaymentsIntegration","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getPaymentsIntegrations":{"http":"GET","path":"/{version}/payments/integrations","dto":"GetPaymentsIntegrations","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false}]},"savePaymentsIntegration":{"http":"POST","path":"/{version}/payments/integrations","dto":"SavePaymentsIntegration","fields":[{"name":"integration","type":"PaymentIntegrationRequest","kind":"json","required":true}]},"testPaymentsIntegration":{"http":"POST","path":"/{version}/payments/integrations/test","dto":"TestPaymentsIntegration","fields":[{"name":"integrationId","type":"string","kind":"string","required":true}]}},"projects":{"getProjectBrief":{"http":"GET","path":"/{version}/projects/{projectId}/ai/brief","dto":"GetProjectBriefRequest","fields":[{"name":"sinceSeq","type":"number","kind":"number","required":false}]},"getWorkItems":{"http":"GET","path":"/{version}/projects/{projectId}/ai/work-items","dto":"GetWorkItemsRequest","fields":[{"name":"status","type":"string","kind":"string","required":false}]},"getWorkItem":{"http":"GET","path":"/{version}/projects/{projectId}/ai/work-items/{WorkItemId}","dto":"GetWorkItemRequest","fields":[{"name":"workItemId","type":"string","kind":"string","required":true}]},"exportWorkItem":{"http":"GET","path":"/{version}/projects/{projectId}/ai/work-items/{WorkItemId}/export.md","dto":"ExportWorkItemRequest","fields":[{"name":"workItemId","type":"string","kind":"string","required":true}]},"markNeedsYouDone":{"http":"POST","path":"/{version}/projects/{projectId}/ai/work-items/{WorkItemId}/needs-you/{Index}/done","dto":"MarkNeedsYouDoneRequest","fields":[{"name":"workItemId","type":"string","kind":"string","required":true},{"name":"index","type":"number","kind":"number","required":true},{"name":"done","type":"boolean","kind":"boolean","required":false}]}},"public":{"getPublicProjectConfig":{"http":"GET","path":"/{version}/public/projects/{ProjectId}/config","dto":"GetPublicProjectConfig","fields":[]},"getPublicProjectLegal":{"http":"GET","path":"/{version}/public/projects/{ProjectId}/legal/{Kind}","dto":"GetPublicProjectLegal","fields":[{"name":"kind","type":"string","kind":"string","required":false}]}},"regions":{},"resources":{"resolveResources":{"http":"POST","path":"/{version}/resources/resolve","dto":"ResolveResources","fields":[{"name":"refs","type":"IReadOnlyList","kind":"json","required":true}]}},"scheduler":{"disableScheduler":{"http":"GET","path":"/{version}/scheduler/disable","dto":"DisableScheduler","fields":[]},"enableScheduler":{"http":"GET","path":"/{version}/scheduler/enable","dto":"EnableScheduler","fields":[]},"deleteSchedulerTask":{"http":"DELETE","path":"/{version}/scheduler/tasks/{Id}","dto":"DeleteSchedulerTask","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"disableSchedulerTask":{"http":"PUT","path":"/{version}/scheduler/tasks/{Id}/disable","dto":"DisableSchedulerTask","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"enableSchedulerTask":{"http":"PUT","path":"/{version}/scheduler/tasks/{Id}/enable","dto":"EnableSchedulerTask","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getSchedulerTask":{"http":"GET","path":"/{version}/scheduler/tasks/{id}","dto":"GetSchedulerTask","fields":[{"name":"id","type":"string","kind":"string","required":true}]},"getSchedulerTasks":{"http":"GET","path":"/{version}/scheduler/tasks","dto":"GetSchedulerTasks","fields":[{"name":"startingAfter","type":"string","kind":"string","required":false},{"name":"endingBefore","type":"string","kind":"string","required":false},{"name":"pageSize","type":"number","kind":"number","required":false},{"name":"paging","type":"PagingArgs","kind":"json","required":false},{"name":"type","type":"SchedulerTaskType","kind":"string","required":false,"options":["EmailCampaign","PushCampaign","SmsCampaign","CodeFunctionalCall","WebhookCall"]},{"name":"enabled","type":"boolean","kind":"boolean","required":false}]},"saveSchedulerTask":{"http":"POST","path":"/{version}/scheduler/tasks","dto":"SaveSchedulerTaskRequest","fields":[{"name":"taskId","type":"string","kind":"string","required":false},{"name":"name","type":"string","kind":"string","required":true},{"name":"description","type":"string","kind":"string","required":false},{"name":"cron","type":"string","kind":"string","required":true},{"name":"initiatorUserId","type":"string","kind":"string","required":true},{"name":"isEnabled","type":"boolean","kind":"boolean","required":true},{"name":"stopOnError","type":"boolean","kind":"boolean","required":true},{"name":"task","type":"SchedulerTaskRequest","kind":"json","required":true}]}},"support":{"closeSupportCase":{"http":"POST","path":"/{version}/support/cases/{CaseId}/close","dto":"CloseSupportCaseRequest","fields":[{"name":"caseId","type":"string","kind":"string","required":true}]},"reopenSupportCase":{"http":"POST","path":"/{version}/support/cases/{CaseId}/reopen","dto":"ReopenSupportCaseRequest","fields":[{"name":"caseId","type":"string","kind":"string","required":true},{"name":"reason","type":"string","kind":"string","required":true}]},"resolveSupportCase":{"http":"POST","path":"/{version}/support/cases/{CaseId}/resolve","dto":"ResolveSupportCaseRequest","fields":[{"name":"caseId","type":"string","kind":"string","required":true},{"name":"resolution","type":"CaseResolutionDto","kind":"json","required":false}]},"appendSupportCaseMessage":{"http":"POST","path":"/{version}/support/cases/{CaseId}/messages","dto":"AppendSupportCaseMessageRequest","fields":[{"name":"caseId","type":"string","kind":"string","required":true},{"name":"message","type":"string","kind":"string","required":true}]},"getSupportCase":{"http":"GET","path":"/{version}/support/cases/{CaseId}","dto":"GetSupportCase","fields":[{"name":"caseId","type":"string","kind":"string","required":true}]},"getSupportCases":{"http":"GET","path":"/{version}/support/cases","dto":"GetSupportCases","fields":[{"name":"pagingArgs","type":"PagingArgs","kind":"json","required":false}]},"openSupportCase":{"http":"POST","path":"/{version}/support/cases","dto":"OpenSupportCaseRequest","fields":[{"name":"kind","type":"string","kind":"string","required":true},{"name":"severity","type":"string","kind":"string","required":true},{"name":"subject","type":"string","kind":"string","required":true},{"name":"message","type":"string","kind":"string","required":true}]}},"webhooks":{"getWebhookIntegration":{"http":"GET","path":"/{version}/webhooks/integration","dto":"GetWebhookIntegration","fields":[]},"revealWebhookIntegrationSecret":{"http":"GET","path":"/{version}/webhooks/integration/secret","dto":"RevealWebhookIntegrationSecretRequest","fields":[]},"rotateWebhookIntegrationSecret":{"http":"POST","path":"/{version}/webhooks/integration/secret/rotate","dto":"RotateWebhookIntegrationSecretRequest","fields":[]},"updateWebhookIntegrationExtraHeaders":{"http":"PUT","path":"/{version}/webhooks/integration/extra-headers","dto":"UpdateWebhookIntegrationExtraHeadersRequest","fields":[]},"receiveWebhook":{"http":"POST","path":"/{version}/webhooks/{source}/{integrationInstanceId}","dto":"ReceiveWebhook","fields":[{"name":"source","type":"string","kind":"string","required":true},{"name":"integrationInstanceId","type":"string","kind":"string","required":true},{"name":"requestStream","type":"string","kind":"string","required":true}]},"disableWebhookDestination":{"http":"PUT","path":"/{version}/webhooks/destinations/{DestinationId}/disable","dto":"DisableWebhookDestinationRequest","fields":[{"name":"destinationId","type":"string","kind":"string","required":true}]},"enableWebhookDestination":{"http":"PUT","path":"/{version}/webhooks/destinations/{DestinationId}/enable","dto":"EnableWebhookDestinationRequest","fields":[{"name":"destinationId","type":"string","kind":"string","required":true}]},"removeWebhookDestination":{"http":"DELETE","path":"/{version}/webhooks/destinations/{DestinationId}","dto":"RemoveWebhookDestinationRequest","fields":[{"name":"destinationId","type":"string","kind":"string","required":true}]},"saveWebhookDestination":{"http":"POST","path":"/{version}/webhooks/destinations","dto":"SaveWebhookDestinationRequest","fields":[{"name":"destinationId","type":"string","kind":"string","required":false},{"name":"destinationName","type":"string","kind":"string","required":true},{"name":"endpointUrl","type":"string","kind":"string","required":true},{"name":"selectedEvents","type":"string[]","kind":"string[]","required":true}]}}}} diff --git a/src/lib/dispatch.ts b/src/lib/dispatch.ts index 9b7be67..71079c4 100644 --- a/src/lib/dispatch.ts +++ b/src/lib/dispatch.ts @@ -10,21 +10,48 @@ * The SDK namespaces are auto-generated and uniform (every method takes one * request object), so the CLI can discover modules and methods at runtime — * every current AND future SDK method is callable without new command files. + * + * Values are typed in two steps: the raw tokens are collected first (the + * method is not known yet), then `typeFields` applies the request-field map + * of the matched method — a `string` field keeps "0042", a `boolean` flag + * does not swallow the word after it. An explicit type wins over both: + * `--field:str 0042`, `--field:num 7`, `--field:bool false`, `--field:json '{...}'`. + * `--body ''` replaces the field flags with one object. + * + * No oclif import here on purpose. */ +import type {FieldKind} from './request-fields.js' + +export type ExplicitType = 'str' | 'num' | 'bool' | 'json' + +export interface RawField { + name: string + /** Undefined when the flag stood alone (`--archived`). */ + value?: string + /** True when the value was the next argv token (not `--name=value`). */ + fromNextToken: boolean + /** `--name:str value` — explicit type, wins over the field map. */ + explicit?: ExplicitType +} + export interface ParsedInvocation { words: string[] positionals: string[] + rawFields: RawField[] + /** Fields typed by the heuristic only — call `typeFields` once the method is known. */ fields: Record yes: boolean dryRun: boolean } +const EXPLICIT_TYPES = new Set(['str', 'num', 'bool', 'json']) + /** Split leftover argv into words, positional values and --field values. */ export function parseArgv(argv: string[]): ParsedInvocation { const words: string[] = [] const positionals: string[] = [] - const fields: Record = {} + const rawFields: RawField[] = [] let yes = false let dryRun = false @@ -37,16 +64,17 @@ export function parseArgv(argv: string[]): ParsedInvocation { } else if (token.startsWith('--')) { const body = token.slice(2) const eq = body.indexOf('=') + const head = eq > 0 ? body.slice(0, eq) : body + const {name, explicit} = splitType(head) if (eq > 0) { - // --field=value syntax - fields[body.slice(0, eq)] = coerce(body.slice(eq + 1)) + rawFields.push({name, value: body.slice(eq + 1), fromNextToken: false, explicit}) } else { const next = argv[i + 1] if (next !== undefined && !next.startsWith('--')) { - fields[body] = coerce(next) + rawFields.push({name, value: next, fromNextToken: true, explicit}) i++ } else { - fields[body] = true + rawFields.push({name, fromNextToken: false, explicit}) } } } else if (/^[a-z][a-z-]*$/i.test(token) && !looksLikeId(token)) { @@ -56,7 +84,111 @@ export function parseArgv(argv: string[]): ParsedInvocation { } } - return {words, positionals, fields, yes, dryRun} + return {words, positionals, rawFields, fields: typeFields(rawFields, {}).fields, yes, dryRun} +} + +/** `name:str` → {name, explicit: 'str'}; a plain name passes through. */ +function splitType(head: string): {name: string; explicit?: ExplicitType} { + const colon = head.lastIndexOf(':') + if (colon > 0) { + const suffix = head.slice(colon + 1) + if (EXPLICIT_TYPES.has(suffix)) return {name: head.slice(0, colon), explicit: suffix as ExplicitType} + } + + return {name: head} +} + +/** + * Apply the request-field kinds of the matched method. A known boolean flag + * that took the next token gives it back as a positional. Unknown fields + * fall back to the old heuristic (true/false/integers are converted). + */ +export function typeFields( + rawFields: RawField[], + kinds: Record, +): {fields: Record; returned: string[]; errors: string[]} { + const fields: Record = {} + const returned: string[] = [] + const errors: string[] = [] + + for (const raw of rawFields) { + const kind = kinds[raw.name] + if (raw.explicit) { + if (raw.value === undefined) { + if (raw.explicit === 'bool') fields[raw.name] = true + else errors.push(`--${raw.name}:${raw.explicit} needs a value`) + continue + } + + const typed = convert(raw.value, raw.explicit, `--${raw.name}:${raw.explicit}`) + if (typed.error) errors.push(typed.error) + else fields[raw.name] = typed.value + continue + } + + if (kind === 'boolean') { + if (raw.value === undefined || !raw.fromNextToken) { + fields[raw.name] = raw.value === undefined ? true : raw.value !== 'false' + } else if (raw.value === 'true' || raw.value === 'false') { + fields[raw.name] = raw.value === 'true' + } else { + // `--archived some_id`: the flag is boolean, the token was not for it. + fields[raw.name] = true + returned.push(raw.value) + } + + continue + } + + if (raw.value === undefined) { + fields[raw.name] = true + continue + } + + if (kind === 'string') fields[raw.name] = raw.value + else if (kind === 'number') { + const n = Number(raw.value) + if (Number.isNaN(n)) errors.push(`--${raw.name} expects a number, got "${raw.value}"`) + else fields[raw.name] = n + } else if (kind === 'json' || kind === 'json[]' || kind === 'string[]' || kind === 'number[]' || kind === 'boolean[]') { + fields[raw.name] = looksLikeJson(raw.value) ? parseJsonOr(raw.value) : kind === 'string[]' ? raw.value.split(',') : coerce(raw.value) + } else fields[raw.name] = coerce(raw.value) + } + + return {fields, returned, errors} +} + +function convert(value: string, type: ExplicitType, label: string): {value?: unknown; error?: string} { + switch (type) { + case 'str': + return {value} + case 'num': { + const n = Number(value) + return Number.isNaN(n) ? {error: `${label} expects a number, got "${value}"`} : {value: n} + } + + case 'bool': + if (value === 'true' || value === 'false') return {value: value === 'true'} + return {error: `${label} expects true or false, got "${value}"`} + default: + try { + return {value: JSON.parse(value)} + } catch { + return {error: `${label} is not valid JSON: ${value.slice(0, 80)}`} + } + } +} + +function looksLikeJson(value: string): boolean { + return /^\s*[[{]/.test(value) +} + +function parseJsonOr(value: string): unknown { + try { + return JSON.parse(value) + } catch { + return value + } } /** IDs look like maggr_..., sch_..., 66b2f0a1..., UPPER env names, etc. */ @@ -64,6 +196,7 @@ function looksLikeId(token: string): boolean { return /[_0-9]/.test(token) || token === token.toUpperCase() } +/** The old heuristic, for fields the map does not know. */ function coerce(value: string): unknown { if (value === 'true') return true if (value === 'false') return false @@ -151,12 +284,19 @@ export function matchMethods( return matches } -const DESTRUCTIVE_VERBS = new Set(['delete', 'remove', 'clean', 'regenerate', 'rotate', 'stop']) +const DESTRUCTIVE_VERBS = new Set(['delete', 'remove', 'clean', 'regenerate', 'rotate', 'stop', 'disable', 'block']) export function isDestructive(method: string): boolean { return DESTRUCTIVE_VERBS.has(camelSplit(method)[0]) } +/** Read-only verbs: no --dry-run needed, nothing changes. */ +const READ_VERBS = new Set(['get', 'list', 'find', 'count', 'search', 'preview', 'render', 'reveal', 'check', 'validate', 'export', 'download', 'ping', 'echo', 'test', 'verify']) + +export function isReadOnly(method: string): boolean { + return READ_VERBS.has(camelSplit(method)[0]) +} + /** * Module aliases so daily language works: `norbix hub db ...`, and * `norbix hub email templates get` routes into the notifications module diff --git a/src/lib/namespace-command.ts b/src/lib/namespace-command.ts index b4c62a0..5ec180f 100644 --- a/src/lib/namespace-command.ts +++ b/src/lib/namespace-command.ts @@ -1,10 +1,25 @@ import {Norbix} from '@norbix.ai/ts' import {BaseCommand, type GlobalFlags} from '../base.js' -import {MODULE_ALIASES, camelSplit, isDestructive, matchMethods, parseArgv} from './dispatch.js' +import {usageError} from './cli-error.js' +import {MODULE_ALIASES, camelSplit, isDestructive, matchMethods, parseArgv, typeFields} from './dispatch.js' +import {readJsonInput} from './json.js' +import {describeFields, fieldKinds, methodInfo, type FieldKind} from './request-fields.js' type SdkModule = Record) => Promise> +export interface NamespaceFlags extends GlobalFlags { + body?: string +} + +/** The text every scope help ends with: how values are typed. */ +export const VALUE_RULES = `Values: words are lowercase letters; anything with digits, "_" or capitals is a +value (the first one becomes "id"). --field value is typed from the SDK's +request type (a string field keeps "0042"; a boolean flag never swallows the +next word). Force a type with --field:str, --field:num, --field:bool or +--field:json. Or pass the whole request as JSON: --body '{"id":"..."}' (or +--body - for stdin) — then no --field flags are allowed.` + /** * Shared engine for `norbix hub ...` and `norbix api ...` — resolves the * module and method from plain words and calls the SDK. @@ -18,7 +33,7 @@ export abstract class NamespaceCommand extends BaseCommand { * known global flags go to oclif, everything else goes to the dispatcher. */ protected splitArgv(): {flagArgv: string[]; rest: string[]; help: boolean} { - const VALUE_FLAGS = new Set(['--project', '--env', '--region', '--api-key', '--account', '--profile']) + const VALUE_FLAGS = new Set(['--project', '--env', '--region', '--api-key', '--account', '--profile', '--body']) const BOOL_FLAGS = new Set(['--json', '--yes', '-y', '--dry-run']) const flagArgv: string[] = [] const rest: string[] = [] @@ -52,6 +67,19 @@ export abstract class NamespaceCommand extends BaseCommand { return [...nouns, verb].join(' ') } + /** What `--json` returns for one method: word form, route and request fields. */ + protected describeMethod(moduleName: string, method: string, hide: Set): Record { + const info = methodInfo(this.target, moduleName, method) + return { + method, + words: this.wordForm(method, moduleName, hide), + destructive: isDestructive(method), + http: info?.http, + path: info?.path, + fields: info?.fields ?? [], + } + } + /** Scoped help: everything callable under `norbix {target} {module} {words...}`. */ protected scopeHelp( moduleWord: string, @@ -74,7 +102,8 @@ export abstract class NamespaceCommand extends BaseCommand { .map((m) => { const form = this.wordForm(m, moduleName, hide) const marker = isDestructive(m) ? ' (asks confirmation)' : '' - return ` norbix ${this.target} ${moduleWord} ${form}${marker}` + const fields = describeFields(methodInfo(this.target, moduleName, m)) + return ` norbix ${this.target} ${moduleWord} ${form}${marker}${fields ? `\n fields: ${fields}` : ''}` }) .join('\n') @@ -83,13 +112,18 @@ export abstract class NamespaceCommand extends BaseCommand { `${scoped.length === 0 ? ' (no exact scope match — showing the whole module)' : ''}:\n\n${rows}\n\n` + `Add an ID as a plain value and request fields as flags:\n` + ` norbix ${this.target} ${moduleWord} ${this.wordForm(list[0], moduleName, hide)} --someField value\n\n` + - `Useful extras: --dry-run (preview the call), --yes (skip confirmation),\n` + + `${VALUE_RULES}\n\n` + + `Useful extras: --dry-run (preview the exact request), --yes (skip confirmation),\n` + `--json (machine output), --profile/--env/--region/--project (context).`, ) - return {scope: `${this.target}.${moduleName}`, commands: list.map((m) => this.wordForm(m, moduleName, hide))} + return { + scope: `${this.target}.${moduleName}`, + commands: list.map((m) => this.describeMethod(moduleName, m, hide)), + valueRules: VALUE_RULES, + } } - protected async dispatch(argv: string[], flags: GlobalFlags, help = false): Promise { + protected async dispatch(argv: string[], flags: NamespaceFlags, help = false): Promise { const parsed = parseArgv(argv) const [moduleWord, ...restWords] = parsed.words @@ -100,7 +134,7 @@ export abstract class NamespaceCommand extends BaseCommand { if (!moduleWord) { this.print( - `Usage: norbix ${this.target} [id] [--field value]\n\nModules:\n ${moduleNames.join('\n ')}\n\n` + + `Usage: norbix ${this.target} [id] [--field value | --body '']\n\nModules:\n ${moduleNames.join('\n ')}\n\n` + `Get help at any level:\n norbix ${this.target} --help\n norbix ${this.target} --help\n\n` + `Example: norbix ${this.target} database aggregates delete maggr_123 --schemaId sch_456`, ) @@ -111,8 +145,10 @@ export abstract class NamespaceCommand extends BaseCommand { const moduleName = alias && namespace[alias.module] ? alias.module : moduleWord const sdkModule = namespace[moduleName] if (!sdkModule) { - this.error( - `Unknown module "${moduleWord}" on ${this.target}.\nAvailable: ${moduleNames.join(', ')}`, + throw usageError( + `Unknown module "${moduleWord}" on ${this.target}.`, + `Available: ${moduleNames.join(', ')}`, + `norbix ${this.target} --help`, ) } @@ -121,50 +157,48 @@ export abstract class NamespaceCommand extends BaseCommand { .filter((k) => typeof sdkModule[k] === 'function') .sort() + const injected = alias?.injectWord && moduleName !== moduleWord ? alias.injectWord : undefined + const hide = new Set(injected ? [injected] : []) + if (help) { - const injected = alias?.injectWord && moduleName !== moduleWord ? alias.injectWord : undefined const helpWords = injected ? [injected, ...restWords] : [...restWords] return this.scopeHelp(moduleWord, moduleName, methods, helpWords, restWords, injected) } - if (restWords.length === 0 && parsed.positionals.length === 0) { + if (restWords.length === 0 && parsed.positionals.length === 0 && parsed.rawFields.length === 0 && !flags.body) { this.print( - `Methods of ${this.target}.${moduleName}:\n ${methods.join('\n ')}\n\nCall one with plain words, e.g.:\n norbix ${this.target} ${moduleWord} aggregates get\n norbix ${this.target} ${moduleWord} aggregates delete `, + `Methods of ${this.target}.${moduleName}:\n ${methods.join('\n ')}\n\nCall one with plain words, e.g.:\n norbix ${this.target} ${moduleWord} aggregates get\n norbix ${this.target} ${moduleWord} aggregates delete \n\nSee fields per method: norbix ${this.target} ${moduleWord} --help (or --json)`, ) - return {module: moduleName, methods} + return {module: moduleName, methods: methods.map((m) => this.describeMethod(moduleName, m, hide))} } const words = [...restWords] - if (alias?.injectWord && moduleName !== moduleWord) words.unshift(alias.injectWord) + if (injected) words.unshift(injected) const matches = matchMethods(methods, words, moduleName) if (matches.length === 0) { // Show only methods sharing at least one word — the full list can be 100+. const lower = words.map((w) => w.toLowerCase().replace(/s$/, '')) const related = methods.filter((m) => lower.some((w) => m.toLowerCase().includes(w))) const shown = (related.length > 0 ? related : methods).slice(0, 25) - this.error( - `No ${this.target}.${moduleName} method matches "${words.join(' ')}".\n` + - `${related.length > 0 ? 'Close methods' : 'Methods'}:\n ${shown.join('\n ')}` + - `${shown.length < methods.length ? `\n … run \`norbix ${this.target} ${moduleWord}\` for the full list` : ''}`, + throw usageError( + `No ${this.target}.${moduleName} method matches "${words.join(' ')}".`, + `${related.length > 0 ? 'Close methods' : 'Methods'}: ${shown.join(', ')}` + + `${shown.length < methods.length ? ` … run \`norbix ${this.target} ${moduleWord}\` for the full list` : ''}`, + `norbix ${this.target} ${moduleWord} --help`, ) } if (matches.length > 1) { - this.error( - `"${words.join(' ')}" is ambiguous — did you mean:\n ${matches.map((m) => m.method).join('\n ')}\nAdd a word (e.g. the verb) to be specific.`, + throw usageError( + `"${words.join(' ')}" is ambiguous — did you mean: ${matches.map((m) => m.method).join(', ')}`, + 'Add a word (e.g. the verb) to be specific, or type the camelCase method name itself.', + `norbix ${this.target} ${moduleWord} --help`, ) } const method = matches[0].method - const request: Record = {...parsed.fields} - if (parsed.positionals.length > 0 && request.id === undefined) { - request.id = parsed.positionals[0] - if (parsed.positionals.length > 1) { - this.error( - `Only one positional value is allowed (became "id"). Pass the rest as flags, e.g. --schemaId ${parsed.positionals[1]}`, - ) - } - } + const info = methodInfo(this.target, moduleName, method) + const request = await this.buildRequest(parsed, flags, info === undefined ? {} : fieldKinds(info), moduleName, method) // Context (auth, region, project) is resolved before any confirmation or // dry run, so a dry run that would fail for real fails here too. @@ -178,4 +212,54 @@ export abstract class NamespaceCommand extends BaseCommand { this.print(res) return res } + + /** The request object: `--body` as a whole, or the typed `--field` flags plus the positional id. */ + private async buildRequest( + parsed: ReturnType, + flags: NamespaceFlags, + kinds: Record, + moduleName: string, + method: string, + ): Promise> { + let request: Record + let positionals = parsed.positionals + + if (flags.body !== undefined) { + if (parsed.rawFields.length > 0) { + throw usageError( + `--body carries the whole request; --${parsed.rawFields[0].name} cannot be combined with it.`, + 'Put every field inside the --body JSON object, or drop --body and use --field flags only.', + `norbix ${this.target} --help`, + ) + } + + const body = JSON.parse(await readJsonInput(flags.body, 'body')) as unknown + if (body === null || typeof body !== 'object' || Array.isArray(body)) { + throw usageError('--body must be a JSON object.', `Example: --body '{"id":"${positionals[0] ?? 'abc123'}"}'`) + } + + request = {...(body as Record)} + } else { + const typed = typeFields(parsed.rawFields, kinds) + if (typed.errors.length > 0) { + throw usageError(typed.errors.join('; '), `Fields of ${method}: ${describeFields(methodInfo(this.target, moduleName, method)) || 'see --help'}`) + } + + request = typed.fields + positionals = [...positionals, ...typed.returned] + } + + if (positionals.length > 0 && request.id === undefined) { + request.id = positionals[0] + if (positionals.length > 1) { + throw usageError( + `Only one positional value is allowed (became "id"). Pass the rest as flags, e.g. --schemaId ${positionals[1]}`, + 'Use --field value for every other request field, or --body for the whole request.', + `norbix ${this.target} --help`, + ) + } + } + + return request + } } diff --git a/src/lib/project.ts b/src/lib/project.ts index 4d8ba16..9456242 100644 --- a/src/lib/project.ts +++ b/src/lib/project.ts @@ -1,6 +1,5 @@ import {readFileSync} from 'node:fs' -import {confirm} from '@inquirer/prompts' import type {Norbix} from '@norbix.ai/ts' import {BaseCommand, type GlobalFlags} from '../base.js' @@ -14,27 +13,20 @@ import {BaseCommand, type GlobalFlags} from '../base.js' * context (`--project`, NORBIX_PROJECT_ID, profile or login session). */ export abstract class ProjectCommand extends BaseCommand { - protected projectClient(flags: GlobalFlags, projectId?: string): {client: Norbix; projectId: string} { + /** + * `reader` is for the read that comes before a write (current origins, + * current settings). A dry run stops every request, so the read goes out + * live — it changes nothing — and only the write is stopped and printed. + */ + protected projectClient( + flags: GlobalFlags, + projectId?: string, + ): {client: Norbix; reader: Norbix; projectId: string} { const client = this.client(flags) + const reader = flags['dry-run'] ? this.client({...flags, 'dry-run': false}) : client const id = projectId ?? this.resolveContext(flags).projectId if (!id) this.error('No project ID configured.\nRun `norbix configure` (or `norbix login`), or pass --project.') - return {client, projectId: id} - } - - /** - * Ask before a change that is hard to undo. `--yes` skips the question. With - * no terminal to ask in (a script, a pipe) the command refuses instead of - * going ahead — a script must say `--yes` on purpose. - */ - protected async confirmOrStop(yes: boolean, message: string): Promise { - if (yes) return true - if (!process.stdin.isTTY || !process.stdout.isTTY) { - this.error(`${message}\nNo terminal to confirm in — pass --yes to go ahead.`) - } - - const ok = await confirm({message, default: false}) - if (!ok) this.print('Cancelled.') - return ok + return {client, reader, projectId: id} } } diff --git a/src/lib/publicFiles.ts b/src/lib/publicFiles.ts index 4520594..7e813a1 100644 --- a/src/lib/publicFiles.ts +++ b/src/lib/publicFiles.ts @@ -42,6 +42,22 @@ export interface PublicFilesResult { /** The Hub version the SDK talks by default. Kept in one place. */ const HUB_VERSION = 'v2' +/** The exact request `callPublicFiles` sends — for `--dry-run`. */ +export function publicFilesRequest( + ctx: ResolvedContext, + operation: PublicFilesOperation, + body: {filesIntegrationId: string; path: string}, +): {method: string; url: string; headers: Record; body: unknown} { + const base = ctx.hubUrl.endsWith('/') ? ctx.hubUrl.slice(0, -1) : ctx.hubUrl + const headers: Record = {Accept: 'application/json', 'Content-Type': 'application/json'} + if (ctx.bearerToken ?? ctx.apiKey) headers.Authorization = 'Bearer ***' + if (ctx.projectId) headers['X-CM-ProjectId'] = ctx.projectId + if (ctx.accountId) headers['X-CM-AccountId'] = ctx.accountId + if (ctx.env && ctx.env !== 'PROD') headers['norbix-env'] = ctx.env + if (ctx.region) headers['nb-region'] = ctx.region + return {method: 'POST', url: `${base}/${HUB_VERSION}/${ROUTES[operation]}`, headers, body} +} + export async function callPublicFiles( ctx: ResolvedContext, operation: PublicFilesOperation, diff --git a/src/lib/request-fields.ts b/src/lib/request-fields.ts new file mode 100644 index 0000000..3ee0511 --- /dev/null +++ b/src/lib/request-fields.ts @@ -0,0 +1,61 @@ +import {readFileSync} from 'node:fs' + +/** + * The request-field map generated from the installed SDK + * (`scripts/gen-request-fields.mjs` → `src/generated/request-fields.json`): + * for every `api.*` / `hub.*` method, its HTTP route and request fields. + * Loaded lazily — only `hub`/`api` and `schema` read it. + * + * No oclif import: the MCP server can list the same fields. + */ + +export type FieldKind = 'string' | 'number' | 'boolean' | 'json' | 'string[]' | 'number[]' | 'boolean[]' | 'json[]' + +export interface RequestField { + name: string + /** The TypeScript type as the SDK declares it (for display). */ + type: string + kind: FieldKind + required: boolean + /** Enum values when the type is an enum. */ + options?: string[] +} + +export interface MethodInfo { + http: string + path: string + dto: string + fields: RequestField[] +} + +export interface RequestFieldMap { + sdk: string + api: Record> + hub: Record> +} + +let cached: RequestFieldMap | undefined + +export function loadRequestFields(): RequestFieldMap { + cached ??= JSON.parse(readFileSync(new URL('../generated/request-fields.json', import.meta.url), 'utf8')) as RequestFieldMap + return cached +} + +export function methodInfo(target: 'api' | 'hub', moduleName: string, method: string): MethodInfo | undefined { + return loadRequestFields()[target][moduleName]?.[method] +} + +/** `name` → kind, for the plain-word parser. */ +export function fieldKinds(info: MethodInfo | undefined): Record { + const out: Record = {} + for (const f of info?.fields ?? []) out[f.name] = f.kind + return out +} + +/** One line per field for help: `id (string, required)`. */ +export function describeFields(info: MethodInfo | undefined): string { + if (!info) return '' + return info.fields + .map((f) => `${f.name} (${f.options ? f.options.join('|') : f.kind}${f.required ? ', required' : ''})`) + .join(', ') +} diff --git a/src/lib/schema.ts b/src/lib/schema.ts new file mode 100644 index 0000000..1aec281 --- /dev/null +++ b/src/lib/schema.ts @@ -0,0 +1,165 @@ +import {EXIT_CODES} from './exit-codes.js' + +/** + * `norbix schema [command] --json`: the whole command surface as data, so an + * agent can discover the CLI without reading source. Built from plain + * descriptors (the command converts oclif's manifest into them) — no oclif + * import here, so the MCP server can emit the same document. + */ + +export interface SchemaArg { + name: string + required: boolean + description?: string + options?: string[] +} + +export interface SchemaFlag { + name: string + char?: string + type: 'boolean' | 'string' | 'integer' | 'option' + description?: string + required?: boolean + multiple?: boolean + options?: string[] + env?: string + default?: unknown + /** True for the context flags every command takes (--project, --profile, --json, ...). */ + global?: boolean +} + +export interface SchemaCommand { + id: string + description: string + /** Asks for confirmation; exits 3 without a terminal unless --yes. */ + destructive: boolean + /** Accepts --dry-run: resolves context, prints the request, sends nothing. */ + supportsDryRun: boolean + args: SchemaArg[] + flags: SchemaFlag[] + examples: string[] +} + +export interface CliSchema { + cli: 'norbix' + version: string + contract: string + exitCodes: Record + commands: SchemaCommand[] + dynamic: {hub: string; api: string; valueRules: string} +} + +/** What the command hands in for every entry of the manifest. */ +export interface CommandDescriptor { + id: string + description?: string + hidden?: boolean + args: Record + flags: Record< + string, + { + name?: string + char?: string + type?: string + description?: string + required?: boolean + multiple?: boolean + options?: string[] + env?: string + default?: unknown + helpGroup?: string + hidden?: boolean + } + > + examples?: Array +} + +export const CONTRACT_PATH = 'docs/agent-contract.md' +export const CONTRACT_URL = 'https://github.com/norbix-code/cli/blob/main/docs/agent-contract.md' + +export const DYNAMIC_HELP = { + hub: 'norbix hub --help lists every method with its request fields; norbix hub --json returns them as data; call with plain words: norbix hub [id] [--field value | --body ]', + api: 'norbix api --help / --json — same engine as hub, for the data-plane API', +} + +export function buildSchema(input: {version: string; bin?: string; commands: CommandDescriptor[]; valueRules: string}): CliSchema { + const bin = input.bin ?? 'norbix' + const commands = input.commands + .filter((c) => !c.hidden) + .map((c) => toSchemaCommand(c, bin)) + .sort((a, b) => a.id.localeCompare(b.id)) + + const exitCodes: CliSchema['exitCodes'] = {} + for (const e of EXIT_CODES) exitCodes[String(e.code)] = {name: e.name, meaning: e.meaning, hint: e.hint} + + return { + cli: 'norbix', + version: input.version, + contract: `${CONTRACT_PATH} (${CONTRACT_URL})`, + exitCodes, + commands, + dynamic: {...DYNAMIC_HELP, valueRules: input.valueRules}, + } +} + +function toSchemaCommand(c: CommandDescriptor, bin: string): SchemaCommand { + const flags = Object.entries(c.flags) + .filter(([, f]) => !f.hidden) + .map(([name, f]) => { + const flag: SchemaFlag = {name: f.name ?? name, type: flagType(f.type, f.options)} + if (f.char) flag.char = f.char + if (f.description) flag.description = f.description + if (f.required) flag.required = true + if (f.multiple) flag.multiple = true + if (f.options) flag.options = f.options + if (f.env) flag.env = f.env + if (f.default !== undefined && f.default !== false) flag.default = f.default + if (f.helpGroup === 'GLOBAL' || name === 'json') flag.global = true + return flag + }) + + const args = Object.entries(c.args).map(([name, a]) => { + const arg: SchemaArg = {name: a.name ?? name, required: Boolean(a.required)} + if (a.description) arg.description = a.description + if (a.options) arg.options = a.options + return arg + }) + + const names = new Set(flags.map((f) => f.name)) + return { + id: c.id.replaceAll(':', ' '), + description: (c.description ?? '').split('\n')[0], + destructive: names.has('yes'), + supportsDryRun: names.has('dry-run'), + args, + flags, + examples: (c.examples ?? []).map((e) => (typeof e === 'string' ? e : e.command).replaceAll('<%= config.bin %>', bin)), + } +} + +function flagType(type: string | undefined, options: string[] | undefined): SchemaFlag['type'] { + if (type === 'boolean') return 'boolean' + if (options && options.length > 0) return 'option' + return 'string' +} + +/** Compact text table for humans. */ +export function formatSchemaTable(schema: CliSchema): string { + const width = Math.max(...schema.commands.map((c) => c.id.length)) + const lines = schema.commands.map((c) => { + const marks = [c.destructive ? 'destructive' : '', c.supportsDryRun ? 'dry-run' : ''].filter(Boolean).join(', ') + return `${c.id.padEnd(width)} ${c.description}${marks ? ` [${marks}]` : ''}` + }) + return [ + `norbix ${schema.version} — ${schema.commands.length} commands. Contract: ${schema.contract}`, + '', + ...lines, + '', + `Dynamic: ${schema.dynamic.hub}`, + ` ${schema.dynamic.api}`, + '', + 'Exit codes: ' + Object.entries(schema.exitCodes).map(([code, e]) => `${code}=${e.name}`).join(' '), + '', + 'Machine form: norbix schema --json, or norbix schema --json for one command.', + ].join('\n') +} diff --git a/tests/__snapshots__/schema.test.ts.snap b/tests/__snapshots__/schema.test.ts.snap new file mode 100644 index 0000000..e1ce592 --- /dev/null +++ b/tests/__snapshots__/schema.test.ts.snap @@ -0,0 +1,2774 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`norbix schema > is stable (snapshot of ids, flags and markers) 1`] = ` +[ + { + "args": [], + "destructive": false, + "flags": [ + "type:string", + "return-url:string", + ], + "id": "account billing-portal", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "account profile", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "account projects", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "account regions", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "account status", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "account team", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "account usage", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "ai llm", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "ai llm default", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "ai llm delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "ai llm disable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "ai llm enable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "provider:option", + "name:string", + "id:string", + "endpoint:string", + "model:string", + "default:boolean", + "disabled:boolean", + "config:string", + ], + "id": "ai llm save", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "ai llm test", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "ai llms", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "ai mcp", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "ai mcp delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "ai mcp disable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "ai mcp enable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "provider:option", + "name:string", + "id:string", + "server-name:string", + "category:string", + "description:string", + "icon:string", + "disabled:boolean", + "config:string", + ], + "id": "ai mcp save", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "ai mcp test", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "ai mcps", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "env:string", + "dry-run:boolean", + "name:string", + "reach:option", + "rights:option", + ], + "id": "ai service-user create", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "ai service-user delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + "keyId", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "ai service-user revoke-key", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "revoke:string", + ], + "id": "ai service-user rotate-key", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "ai service-users", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + "body:string", + ], + "id": "api", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "apikeys list", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "apikeys regenerate", + "supportsDryRun": true, + }, + { + "args": [ + "key", + ], + "destructive": false, + "flags": [], + "id": "config get", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "config list", + "supportsDryRun": false, + }, + { + "args": [ + "key", + "value", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "config set", + "supportsDryRun": true, + }, + { + "args": [ + "key", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "config unset", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "api-url:string", + "hub-url:string", + ], + "id": "configure", + "supportsDryRun": false, + }, + { + "args": [ + "collection", + ], + "destructive": false, + "flags": [ + "pipeline:string", + "id:string", + "tokens:string", + ], + "id": "db aggregate", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "schema:string", + ], + "id": "db aggregates", + "supportsDryRun": false, + }, + { + "args": [ + "collection", + ], + "destructive": false, + "flags": [ + "filter:string", + ], + "id": "db count", + "supportsDryRun": false, + }, + { + "args": [ + "collection", + ], + "destructive": true, + "flags": [ + "id:string", + "filter:string", + "many:boolean", + "yes:boolean", + "dry-run:boolean", + ], + "id": "db delete", + "supportsDryRun": true, + }, + { + "args": [ + "collection", + "field", + ], + "destructive": false, + "flags": [ + "filter:string", + ], + "id": "db distinct", + "supportsDryRun": false, + }, + { + "args": [ + "collection", + ], + "destructive": false, + "flags": [ + "filter:string", + "page-size:string", + "after:string", + "before:string", + ], + "id": "db find", + "supportsDryRun": false, + }, + { + "args": [ + "collection", + "id", + ], + "destructive": false, + "flags": [], + "id": "db get", + "supportsDryRun": false, + }, + { + "args": [ + "collection", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "doc:string", + ], + "id": "db insert", + "supportsDryRun": true, + }, + { + "args": [ + "collection", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "docs:string", + ], + "id": "db insert-many", + "supportsDryRun": true, + }, + { + "args": [ + "collection", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "id:string", + "doc:string", + ], + "id": "db replace", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "db schema", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "db schemas", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "db taxonomies", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "db taxonomy", + "supportsDryRun": false, + }, + { + "args": [ + "collection", + ], + "destructive": true, + "flags": [ + "id:string", + "filter:string", + "update:string", + "many:boolean", + "yes:boolean", + "dry-run:boolean", + ], + "id": "db update", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "email archive", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "stats:boolean", + ], + "id": "email campaign", + "supportsDryRun": false, + }, + { + "args": [ + "id", + "batchId", + "[notificationId]", + ], + "destructive": false, + "flags": [ + "database-integration:string", + "page-size:string", + "after:string", + ], + "id": "email campaign batch", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "batch:string", + "email:string", + "database-integration:string", + "page-size:string", + "after:string", + ], + "id": "email campaign batches", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "template:string", + "audience:option", + "user:string", + "email:string", + "cc:string", + "bcc:string", + "one-each:boolean", + "role:string", + "tag:string", + "schema:string", + "field:string", + "field-type:option", + "integration:string", + "validation-integration:string", + "language:string", + "notes:string", + "at:string", + "token:string", + "database-integration:string", + "config:string", + ], + "id": "email campaign create", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "email campaign delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "batch:string", + "database-integration:string", + "page-size:string", + "after:string", + ], + "id": "email campaign messages", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "email campaigns", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "email clone", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "email delete", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "email disable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "email disable-dependencies", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "email enable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "email footer", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "email footer delete", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "id:string", + "language:string", + "content:string", + "content-file:string", + "translations:string", + ], + "id": "email footer save", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "email footers", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "email integration", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "email integration confirm-delivery", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "email integration default", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "email integration delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "email integration disable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "email integration domain-health", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "email integration enable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "provider:option", + "name:string", + "id:string", + "from:string", + "sender-name:string", + "disabled:boolean", + "config:string", + ], + "id": "email integration save", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "integration:string", + "to:string", + ], + "id": "email integration test", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "email integrations", + "supportsDryRun": false, + }, + { + "args": [ + "[hash]", + ], + "destructive": false, + "flags": [ + "hash:string", + "notification:string", + ], + "id": "email preview", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "id:string", + ], + "id": "email settings", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "email signature", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "email signature delete", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "id:string", + "language:string", + "content:string", + "content-file:string", + "translations:string", + ], + "id": "email signature save", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "email signatures", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "email stop", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "email system-template", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "tag:string", + "theme:string", + "channel:option", + "trigger:option", + "page-size:string", + "after:string", + ], + "id": "email system-templates", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "email template", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "file-ref:string", + "language:string", + ], + "id": "email template attach", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "description:string", + "channel:option", + "tag:string", + "language:string", + "subject:string", + "body:string", + "body-file:string", + "engine:option", + "translations:string", + ], + "id": "email template create", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "code:string", + "file:string", + "token:string", + "preview:boolean", + ], + "id": "email template render", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "email template tokens", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "description:string", + "channel:option", + "tag:string", + "language:string", + "subject:string", + "body:string", + "body-file:string", + "engine:option", + "translations:string", + ], + "id": "email template update", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "archived:boolean", + ], + "id": "email templates", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "email unarchive", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "provider:option", + "name:string", + "id:string", + "disabled:boolean", + "config:string", + ], + "id": "email validation save", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "email validation test", + "supportsDryRun": true, + }, + { + "args": [ + "name", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "env delete", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "env list", + "supportsDryRun": false, + }, + { + "args": [ + "name", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "env use", + "supportsDryRun": true, + }, + { + "args": [ + "remote", + ], + "destructive": true, + "flags": [ + "integration:string", + "yes:boolean", + "dry-run:boolean", + ], + "id": "files delete", + "supportsDryRun": true, + }, + { + "args": [ + "remote", + "[file]", + ], + "destructive": false, + "flags": [ + "integration:string", + ], + "id": "files download", + "supportsDryRun": false, + }, + { + "args": [ + "remote", + ], + "destructive": false, + "flags": [ + "integration:string", + ], + "id": "files info", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "files integrations test", + "supportsDryRun": false, + }, + { + "args": [ + "[path]", + ], + "destructive": false, + "flags": [ + "integration:string", + ], + "id": "files list", + "supportsDryRun": false, + }, + { + "args": [ + "remote", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "folder:boolean", + "integration:string", + ], + "id": "files publish", + "supportsDryRun": true, + }, + { + "args": [ + "remote", + ], + "destructive": false, + "flags": [ + "integration:string", + "expires:string", + ], + "id": "files sign", + "supportsDryRun": false, + }, + { + "args": [ + "remote", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "folder:boolean", + "integration:string", + ], + "id": "files unpublish", + "supportsDryRun": true, + }, + { + "args": [ + "file", + "[remote]", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "integration:string", + "content-type:string", + ], + "id": "files upload", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + "body:string", + ], + "id": "hub", + "supportsDryRun": true, + }, + { + "args": [ + "module", + ], + "destructive": false, + "flags": [], + "id": "integrations", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "user:string", + "password:string", + ], + "id": "login", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "logout", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "level:string", + "module:string", + "search:string", + "event-code:string", + "correlation:string", + "from:string", + "to:string", + "page-size:string", + "after:string", + ], + "id": "logs list", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "logs settings", + "supportsDryRun": false, + }, + { + "args": [ + "correlationId", + ], + "destructive": false, + "flags": [], + "id": "logs trail", + "supportsDryRun": false, + }, + { + "args": [ + "action", + "name", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "module", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "payments disable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "payments enable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "payments integrations", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "payments trigger", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "payments triggers", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "profiles", + "supportsDryRun": false, + }, + { + "args": [ + "[id]", + ], + "destructive": false, + "flags": [], + "id": "project", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "project admin-portal disable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project admin-portal enable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project admin-portal set-service-user", + "supportsDryRun": true, + }, + { + "args": [ + "[url]", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "clear:boolean", + ], + "id": "project admin-portal set-url", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "project admin-portal structure", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "welcome:string", + "system-prompt:string", + "system-prompt-file:string", + "toolset:string", + "llm:string", + "model:string", + "memory:boolean", + "rag-source:string", + "default:boolean", + ], + "id": "project ai assistant create", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "project ai assistant delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "welcome:string", + "system-prompt:string", + "system-prompt-file:string", + "toolset:string", + "llm:string", + "model:string", + "memory:boolean", + "rag-source:string", + "default:boolean", + ], + "id": "project ai assistant update", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "project ai settings", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "enable:boolean", + "disable:boolean", + "llm:string", + "model:string", + ], + "id": "project ai settings set", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "top:string", + ], + "id": "project ai usage", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "project cors", + "supportsDryRun": false, + }, + { + "args": [ + "origin", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project cors add", + "supportsDryRun": true, + }, + { + "args": [ + "origin", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + "remove-admin-portal-origin:boolean", + ], + "id": "project cors remove", + "supportsDryRun": true, + }, + { + "args": [ + "origin", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "remove-admin-portal-origin:boolean", + ], + "id": "project cors set", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "project delete", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "project disable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project enable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project legal expose", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project legal hide", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "terms-file:string", + "privacy-file:string", + "clear-terms:boolean", + "clear-privacy:boolean", + ], + "id": "project legal set", + "supportsDryRun": true, + }, + { + "args": [ + "kind", + ], + "destructive": false, + "flags": [], + "id": "project legal show", + "supportsDryRun": false, + }, + { + "args": [ + "[id]", + ], + "destructive": false, + "flags": [], + "id": "project public-config", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "main:string", + "accent:string", + ], + "id": "project set-colors", + "supportsDryRun": true, + }, + { + "args": [ + "language", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project set-default-language", + "supportsDryRun": true, + }, + { + "args": [ + "[description]", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "clear:boolean", + ], + "id": "project set-description", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "file-resource:string", + "clear:boolean", + ], + "id": "project set-icon", + "supportsDryRun": true, + }, + { + "args": [ + "language", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project set-languages", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "file-resource:string", + "clear:boolean", + ], + "id": "project set-logo", + "supportsDryRun": true, + }, + { + "args": [ + "name", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "project set-name", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "primary:string", + "additional:string", + "clear-additional:boolean", + ], + "id": "project set-regions", + "supportsDryRun": true, + }, + { + "args": [ + "[url]", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "clear:boolean", + ], + "id": "project set-url", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "initiator:string", + "recipient:string", + "target-user:string", + ], + "id": "project tokens", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "push archive", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "stats:boolean", + ], + "id": "push campaign", + "supportsDryRun": false, + }, + { + "args": [ + "id", + "batchId", + "[notificationId]", + ], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "push campaign batch", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "push campaign batches", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "template:string", + "audience:option", + "user:string", + "role:string", + "tag:string", + "schema:string", + "field:string", + "field-type:option", + "device:string", + "integration:string", + "language:string", + "notes:string", + "at:string", + "token:string", + "database-integration:string", + "config:string", + ], + "id": "push campaign create", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "push campaign delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + "messageId", + ], + "destructive": false, + "flags": [ + "batch:string", + ], + "id": "push campaign message", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "batch:string", + "page-size:string", + "after:string", + ], + "id": "push campaign messages", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "push campaigns", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "push clone", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "push delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "push device", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "user:string", + "token:string", + "os:string", + "device-id:string", + "model:string", + "name:string", + ], + "id": "push device register", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "user:string", + "token:string", + "platform:option", + ], + "id": "push devices", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "push disable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "push disable-dependencies", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "push enable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "push integration", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "push integration confirm-delivery", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "push integration default", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "push integration delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "push integration disable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "push integration enable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "provider:option", + "name:string", + "id:string", + "disabled:boolean", + "config:string", + ], + "id": "push integration save", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "integration:string", + "token:string", + "family:option", + ], + "id": "push integration test", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "push integrations", + "supportsDryRun": false, + }, + { + "args": [ + "[hash]", + ], + "destructive": false, + "flags": [ + "hash:string", + ], + "id": "push preview", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "id:string", + ], + "id": "push settings", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "push stop", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "push template", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "description:string", + "channel:option", + "tag:string", + "language:string", + "title:string", + "body:string", + "translations:string", + ], + "id": "push template create", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "code:string", + "token:string", + "preview:boolean", + ], + "id": "push template render", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "push template tokens", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "description:string", + "channel:option", + "tag:string", + "language:string", + "title:string", + "body:string", + "translations:string", + ], + "id": "push template update", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "archived:boolean", + ], + "id": "push templates", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "push unarchive", + "supportsDryRun": true, + }, + { + "args": [ + "path", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "api:boolean", + "method:option", + "body:string", + "query:string", + ], + "id": "raw", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "scheduler delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "scheduler disable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "scheduler enable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "scheduler get", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "scheduler list", + "supportsDryRun": false, + }, + { + "args": [ + "[command]", + ], + "destructive": false, + "flags": [], + "id": "schema", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "sms archive", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "stats:boolean", + ], + "id": "sms campaign", + "supportsDryRun": false, + }, + { + "args": [ + "id", + "batchId", + "[notificationId]", + ], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "sms campaign batch", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "sms campaign batches", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "template:string", + "audience:option", + "user:string", + "role:string", + "tag:string", + "schema:string", + "field:string", + "field-type:option", + "phone:string", + "language:string", + "at:string", + "respect-time-zone:option", + "token:string", + "database-integration:string", + "config:string", + ], + "id": "sms campaign create", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "sms campaign delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + "messageId", + ], + "destructive": false, + "flags": [ + "batch:string", + ], + "id": "sms campaign message", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "batch:string", + "page-size:string", + "after:string", + ], + "id": "sms campaign messages", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "sms campaigns", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "sms clone", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "sms delete", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "sms disable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "sms disable-dependencies", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "sms enable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "sms integration", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "sms integration confirm-delivery", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "sms integration default", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "sms integration delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "sms integration disable", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "sms integration enable", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "provider:option", + "name:string", + "id:string", + "disabled:boolean", + "config:string", + ], + "id": "sms integration save", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "integration:string", + "to:string", + ], + "id": "sms integration test", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "page-size:string", + "after:string", + ], + "id": "sms integrations", + "supportsDryRun": false, + }, + { + "args": [ + "[hash]", + ], + "destructive": false, + "flags": [ + "hash:string", + ], + "id": "sms preview", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "id:string", + ], + "id": "sms settings", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "sms stop", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "sms template", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "description:string", + "channel:option", + "tag:string", + "language:string", + "subject:string", + "body:string", + "translations:string", + ], + "id": "sms template create", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "code:string", + "token:string", + "preview:boolean", + ], + "id": "sms template render", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "sms template tokens", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + "name:string", + "description:string", + "channel:option", + "tag:string", + "language:string", + "subject:string", + "body:string", + "translations:string", + ], + "id": "sms template update", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "archived:boolean", + ], + "id": "sms templates", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "sms unarchive", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "users block", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "users delete", + "supportsDryRun": true, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [], + "id": "users get", + "supportsDryRun": false, + }, + { + "args": [ + "email", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "users invite", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [ + "role:string", + "permissions:boolean", + "page-size:string", + "after:string", + ], + "id": "users list", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "users policies", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "users roles", + "supportsDryRun": false, + }, + { + "args": [ + "id", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "users unblock", + "supportsDryRun": true, + }, + { + "args": [ + "destinationId", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "webhooks disable", + "supportsDryRun": true, + }, + { + "args": [ + "destinationId", + ], + "destructive": false, + "flags": [ + "dry-run:boolean", + ], + "id": "webhooks enable", + "supportsDryRun": true, + }, + { + "args": [ + "destinationId", + ], + "destructive": true, + "flags": [ + "yes:boolean", + "dry-run:boolean", + ], + "id": "webhooks remove", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": true, + "flags": [ + "rotate:boolean", + "yes:boolean", + "dry-run:boolean", + ], + "id": "webhooks secret", + "supportsDryRun": true, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "webhooks show", + "supportsDryRun": false, + }, + { + "args": [], + "destructive": false, + "flags": [], + "id": "whoami", + "supportsDryRun": false, + }, +] +`; diff --git a/tests/agent-mode.test.ts b/tests/agent-mode.test.ts index a894601..f0ae997 100644 --- a/tests/agent-mode.test.ts +++ b/tests/agent-mode.test.ts @@ -53,6 +53,26 @@ const DESTRUCTIVE: string[][] = [ ['db', 'update', 'orders', '--many', '--filter', '{}', '--update', '{"$set":{"a":1}}'], ['api', 'membership', 'user', 'delete', 'abc123'], ['hub', 'scheduler', 'task', 'delete', 'abc123'], + ['ai', 'llm', 'delete', 'abc123'], + ['ai', 'llm', 'disable', 'abc123'], + ['ai', 'mcp', 'delete', 'abc123'], + ['ai', 'mcp', 'disable', 'abc123'], + ['ai', 'service-user', 'delete', 'abc123'], + ['ai', 'service-user', 'revoke-key', 'abc123', 'key_1'], + ['email', 'disable'], + ['email', 'campaign', 'delete', 'abc123'], + ['email', 'footer', 'delete', 'abc123'], + ['email', 'signature', 'delete', 'abc123'], + ['email', 'integration', 'delete', 'abc123'], + ['email', 'integration', 'disable', 'abc123'], + ['sms', 'disable'], + ['sms', 'campaign', 'delete', 'abc123'], + ['sms', 'integration', 'delete', 'abc123'], + ['sms', 'integration', 'disable', 'abc123'], + ['project', 'delete'], + ['project', 'disable'], + ['project', 'admin-portal', 'disable'], + ['project', 'ai', 'assistant', 'delete', 'abc123'], ] describe('destructive commands in a non-interactive shell', () => { @@ -274,3 +294,54 @@ describe('plain output', () => { expect(r.stdout.trim()).toBe('User abc123 deleted.') }) }) + +describe('--dry-run on non-destructive mutating commands', () => { + it('users invite: prints the call and sends nothing', async () => { + const r = await cli(home, ['users', 'invite', 'alice@example.com', '--profile', 'x', '--dry-run', '--json']) + expect(r.code).toBe(0) + const doc = parseSingleJson(r.stdout) as {method: string; request: unknown; http: {method: string}} + expect(doc.method).toBe('api.membership.inviteUser') + expect(doc.request).toEqual({email: 'alice@example.com'}) + expect(doc.http.method).toBe('POST') + expect(gateway.hits).toEqual([]) + }) + + it('files publish: shows the raw hub request it would send', async () => { + const r = await cli(home, ['files', 'publish', 'invoices/2026/invoice.pdf', '--integration', 'fi_1', '--profile', 'x', '--dry-run', '--json']) + expect(r.code).toBe(0) + const doc = parseSingleJson(r.stdout) as {method: string; http: {method: string; url: string; headers: Record; body: unknown}} + expect(doc.method).toBe('hub.files.makeFilePublic') + expect(doc.http.method).toBe('POST') + expect(doc.http.url).toMatch(/\/v2\/files\/item\/public$/) + expect(doc.http.headers.Authorization).toBe('Bearer ***') + expect(doc.http.body).toEqual({filesIntegrationId: 'fi_1', path: 'invoices/2026/invoice.pdf'}) + expect(gateway.hits).toEqual([]) + }) + + it('config set: shows the file it would write and writes nothing', async () => { + const r = await cli(home, ['config', 'set', 'region', 'nb-eu-germany', '--dry-run', '--json']) + expect(r.code).toBe(0) + const doc = parseSingleJson(r.stdout) as {dryRun: boolean; method: string; request: {key: string; value: string; file: string}} + expect(doc).toMatchObject({dryRun: true, method: 'config.set', request: {key: 'region', value: 'nb-eu-germany'}}) + const after = await cli(home, ['config', 'get', 'region', '--json']) + expect(parseSingleJson(after.stdout)).toEqual({key: 'region'}) + }) + + it('hub: a boolean field never swallows the id, a string field keeps its zeros', async () => { + const r = await cli(home, ['hub', 'email', 'templates', 'get', '--showArchived', 'tpl_1', '--templateId:str', '0042', '--profile', 'x', '--dry-run', '--json']) + expect(r.code).toBe(0) + const doc = parseSingleJson(r.stdout) as {request: unknown} + expect(doc.request).toEqual({showArchived: true, templateId: '0042', id: 'tpl_1'}) + }) + + it('hub --body: the whole request as JSON, no mixing with --field', async () => { + const ok = await cli(home, ['hub', 'scheduler', 'task', 'delete', '--body', '{"id":"abc123"}', '--profile', 'x', '--dry-run', '--json']) + expect(ok.code).toBe(0) + expect((parseSingleJson(ok.stdout) as {request: unknown}).request).toEqual({id: 'abc123'}) + + const mixed = await cli(home, ['hub', 'scheduler', 'task', 'delete', '--body', '{"id":"abc123"}', '--name', 'x', '--profile', 'x', '--json']) + expect(mixed.code).toBe(2) + expect((parseSingleJson(mixed.stdout) as {error: {message: string}}).error.message).toMatch(/--body carries the whole request/) + expect(gateway.hits).toEqual([]) + }) +}) diff --git a/tests/dispatch.test.ts b/tests/dispatch.test.ts new file mode 100644 index 0000000..fda849f --- /dev/null +++ b/tests/dispatch.test.ts @@ -0,0 +1,78 @@ +import {describe, expect, it} from 'vitest' + +import {isDestructive, matchMethods, parseArgv, typeFields} from '../src/lib/dispatch.js' + +describe('parseArgv', () => { + it('splits words, the id and the fields', () => { + const p = parseArgv(['database', 'aggregates', 'delete', 'maggr_123', '--schemaId', 'sch_456']) + expect(p.words).toEqual(['database', 'aggregates', 'delete']) + expect(p.positionals).toEqual(['maggr_123']) + expect(p.rawFields).toEqual([{name: 'schemaId', value: 'sch_456', fromNextToken: true, explicit: undefined}]) + }) + + it('reads an explicit type suffix', () => { + const p = parseArgv(['--userId:str', '0042', '--count:num=7', '--archived:bool']) + expect(p.rawFields).toEqual([ + {name: 'userId', value: '0042', fromNextToken: true, explicit: 'str'}, + {name: 'count', value: '7', fromNextToken: false, explicit: 'num'}, + {name: 'archived', fromNextToken: false, explicit: 'bool'}, + ]) + }) +}) + +describe('typeFields', () => { + it('keeps a string field as text even when it looks like a number', () => { + const {fields} = typeFields(parseArgv(['--userId', '0042']).rawFields, {userId: 'string'}) + expect(fields).toEqual({userId: '0042'}) + }) + + it('converts a number field and rejects text in it', () => { + expect(typeFields(parseArgv(['--pageSize', '100']).rawFields, {pageSize: 'number'}).fields).toEqual({pageSize: 100}) + expect(typeFields(parseArgv(['--pageSize', 'many']).rawFields, {pageSize: 'number'}).errors).toEqual(['--pageSize expects a number, got "many"']) + }) + + it('does not let a boolean flag swallow the next word', () => { + const {fields, returned} = typeFields(parseArgv(['--showArchived', 'tpl_1']).rawFields, {showArchived: 'boolean'}) + expect(fields).toEqual({showArchived: true}) + expect(returned).toEqual(['tpl_1']) + }) + + it('still takes true/false for a boolean flag', () => { + expect(typeFields(parseArgv(['--showArchived', 'false']).rawFields, {showArchived: 'boolean'}).fields).toEqual({showArchived: false}) + }) + + it('lets an explicit type win over the map and the heuristic', () => { + const {fields} = typeFields(parseArgv(['--id:str', '0042', '--n:num', '7', '--ok:bool', 'false', '--doc:json', '{"a":1}']).rawFields, {id: 'number'}) + expect(fields).toEqual({id: '0042', n: 7, ok: false, doc: {a: 1}}) + }) + + it('reports a bad explicit value', () => { + expect(typeFields(parseArgv(['--n:num', 'x']).rawFields, {}).errors).toEqual(['--n:num expects a number, got "x"']) + expect(typeFields(parseArgv(['--doc:json', '{bad']).rawFields, {}).errors[0]).toMatch(/not valid JSON/) + }) + + it('falls back to the old heuristic for unknown fields', () => { + expect(typeFields(parseArgv(['--a', 'true', '--b', '42', '--c', 'x']).rawFields, {}).fields).toEqual({a: true, b: 42, c: 'x'}) + }) + + it('parses JSON for object fields and splits string lists', () => { + const {fields} = typeFields(parseArgv(['--paging', '{"pageSize":5}', '--tags', 'a,b']).rawFields, {paging: 'json', tags: 'string[]'}) + expect(fields).toEqual({paging: {pageSize: 5}, tags: ['a', 'b']}) + }) +}) + +describe('method matching', () => { + const methods = ['getSchedulerTask', 'getSchedulerTasks', 'deleteSchedulerTask', 'disableSchedulerTask', 'enableSchedulerTask'] + + it('plural lists, singular gets one', () => { + expect(matchMethods(methods, ['tasks', 'get'], 'scheduler').map((m) => m.method)).toEqual(['getSchedulerTasks']) + expect(matchMethods(methods, ['task', 'get'], 'scheduler').map((m) => m.method)).toEqual(['getSchedulerTask']) + }) + + it('treats delete, disable and block as destructive', () => { + expect(isDestructive('deleteSchedulerTask')).toBe(true) + expect(isDestructive('disableSchedulerTask')).toBe(true) + expect(isDestructive('blockUser')).toBe(true) + expect(isDestructive('enableSchedulerTask')).toBe(false) + }) +}) diff --git a/tests/email-routes.test.ts b/tests/email-routes.test.ts index 0f86874..4ebac2e 100644 --- a/tests/email-routes.test.ts +++ b/tests/email-routes.test.ts @@ -63,7 +63,7 @@ const routes: Array<[string[], string, string]> = [ [['integration', ID], 'GET', `${P}/integrations/${ID}`], [['integration', 'save', '--provider', 'Fake'], 'POST', `${P}/integrations`], [['integration', 'enable', ID], 'PUT', `${P}/integrations/${ID}/enable`], - [['integration', 'disable', ID], 'PUT', `${P}/integrations/${ID}/disable`], + [['integration', 'disable', ID, '--yes'], 'PUT', `${P}/integrations/${ID}/disable`], [['integration', 'default', ID], 'PUT', `${P}/integrations/${ID}/default`], [['integration', 'delete', ID, '--yes'], 'DELETE', `${P}/integrations/${ID}`], [['integration', 'test', '--integration', ID, '--to', TO], 'POST', `${P}/integrations/test`], diff --git a/tests/email.test.ts b/tests/email.test.ts index affcea2..e12b87a 100644 --- a/tests/email.test.ts +++ b/tests/email.test.ts @@ -81,7 +81,7 @@ describe('email integrations', () => { const byId = [ {command: EmailIntegrationEnable, argv: [ID], method: 'enableEmailIntegration', request: {id: ID}, output: `Integration ${ID} enabled.`}, - {command: EmailIntegrationDisable, argv: [ID], method: 'disableEmailIntegration', request: {id: ID}, output: `Integration ${ID} disabled.`}, + {command: EmailIntegrationDisable, argv: [ID, '--yes'], method: 'disableEmailIntegration', request: {id: ID}, output: `Integration ${ID} disabled.`}, {command: EmailIntegrationDefault, argv: [ID], method: 'setEmailsIntegrationAsDefault', request: {id: ID}, output: `Integration ${ID} is now the default.`}, {command: EmailIntegrationDelete, argv: [ID, '--yes'], method: 'deleteEmailIntegration', request: {id: ID}, output: `Integration ${ID} deleted.`}, { diff --git a/tests/project-routes.test.ts b/tests/project-routes.test.ts index 201e967..85c2758 100644 --- a/tests/project-routes.test.ts +++ b/tests/project-routes.test.ts @@ -126,7 +126,7 @@ const routes: Array<[string[], Route[]]> = [ [['project', 'cors'], [['GET', P]]], [['project', 'cors', 'set', 'https://a.example.com'], [['PATCH', `${S}/origins`]]], [['project', 'cors', 'add', 'https://b.example.com'], [['GET', P], ['PATCH', `${S}/origins`]]], - [['project', 'cors', 'remove', 'https://app.example.com'], [['GET', P], ['PATCH', `${S}/origins`]]], + [['project', 'cors', 'remove', 'https://app.example.com', '--yes'], [['GET', P], ['PATCH', `${S}/origins`]]], // Admin portal [['project', 'admin-portal', 'enable'], [['PUT', `${P}/admin-portal/enabled`]]], @@ -156,7 +156,7 @@ const routes: Array<[string[], Route[]]> = [ [['ai', 'llm', 'save', '--provider', 'OpenAI', '--name', 'OpenAI'], [['POST', `${LLM}/`]]], [['ai', 'llm', 'test', ID], [['POST', `${LLM}/test`]]], [['ai', 'llm', 'enable', ID], [['PUT', `${LLM}/${ID}/enable`]]], - [['ai', 'llm', 'disable', ID], [['PUT', `${LLM}/${ID}/disable`]]], + [['ai', 'llm', 'disable', ID, '--yes'], [['PUT', `${LLM}/${ID}/disable`]]], [['ai', 'llm', 'default', ID], [['PUT', `${LLM}/${ID}/default`]]], [['ai', 'llm', 'delete', ID, '--yes'], [['DELETE', `${LLM}/${ID}`]]], @@ -166,7 +166,7 @@ const routes: Array<[string[], Route[]]> = [ [['ai', 'mcp', 'save', '--provider', 'GitHub', '--name', 'GitHub'], [['POST', `${MCP}/`]]], [['ai', 'mcp', 'test', ID], [['POST', `${MCP}/test`]]], [['ai', 'mcp', 'enable', ID], [['PUT', `${MCP}/${ID}/enable`]]], - [['ai', 'mcp', 'disable', ID], [['PUT', `${MCP}/${ID}/disable`]]], + [['ai', 'mcp', 'disable', ID, '--yes'], [['PUT', `${MCP}/${ID}/disable`]]], [['ai', 'mcp', 'delete', ID, '--yes'], [['DELETE', `${MCP}/${ID}`]]], // AI service users (account-level) @@ -250,7 +250,7 @@ describe('project settings bodies', () => { it('delete refuses without --yes when there is no terminal', async () => { const {error} = await runCommand(['project', 'delete', ...globalArgs]) - expect(error?.message).toMatch(/pass --yes/) + expect((error as {code?: string} | undefined)?.code).toBe('CONFIRMATION_REQUIRED') expect(calls).toHaveLength(0) }) }) @@ -279,7 +279,7 @@ describe('project cors bodies', () => { }) it('remove keeps the rest', async () => { - expect(await bodyOf(['project', 'cors', 'remove', 'https://app.example.com'])).toEqual({ + expect(await bodyOf(['project', 'cors', 'remove', 'https://app.example.com', '--yes'])).toEqual({ origins: ['https://pr_test-project.admin.norbix.ai'], }) }) diff --git a/tests/schema.test.ts b/tests/schema.test.ts new file mode 100644 index 0000000..1bd28d0 --- /dev/null +++ b/tests/schema.test.ts @@ -0,0 +1,146 @@ +import {readdirSync, statSync} from 'node:fs' +import {join} from 'node:path' +import {fileURLToPath} from 'node:url' + +import {afterAll, beforeAll, describe, expect, it} from 'vitest' + +import {cli, makeHome, parseSingleJson, startFakeGateway, type FakeGateway} from './_cli.js' + +/** Every command file under dist/commands, as the id a user types. */ +function commandIdsOnDisk(): string[] { + const root = fileURLToPath(new URL('../dist/commands', import.meta.url)) + const walk = (dir: string, prefix: string[]): string[] => + readdirSync(dir).flatMap((name) => { + const full = join(dir, name) + if (statSync(full).isDirectory()) return walk(full, [...prefix, name]) + return name.endsWith('.js') ? [[...prefix, name.slice(0, -3)].join(' ')] : [] + }) + return walk(root, []).sort() +} + +interface SchemaDoc { + cli: string + version: string + contract: string + exitCodes: Record + commands: Array<{ + id: string + description: string + destructive: boolean + supportsDryRun: boolean + args: Array<{name: string; required: boolean}> + flags: Array<{name: string; type: string; global?: boolean; env?: string}> + examples: string[] + }> + dynamic: {hub: string; api: string; valueRules: string} +} + +let gateway: FakeGateway +let home: string + +beforeAll(async () => { + gateway = await startFakeGateway() + home = makeHome(gateway.port) +}) + +afterAll(async () => { + await gateway.close() +}) + +describe('norbix schema', () => { + let schema: SchemaDoc + + beforeAll(async () => { + const r = await cli(home, ['schema', '--json']) + expect(r.code).toBe(0) + expect(r.stderr).toBe('') + schema = parseSingleJson(r.stdout) as SchemaDoc + }) + + it('lists every command in dist/commands', () => { + expect(schema.commands.map((c) => c.id)).toEqual(commandIdsOnDisk()) + }) + + it('marks every command with destructive and supportsDryRun booleans', () => { + for (const c of schema.commands) { + expect(typeof c.destructive, c.id).toBe('boolean') + expect(typeof c.supportsDryRun, c.id).toBe('boolean') + if (c.destructive) expect(c.supportsDryRun, `${c.id} is destructive so it must support --dry-run`).toBe(true) + } + }) + + it('names the contract and the exit codes', () => { + expect(schema.cli).toBe('norbix') + expect(schema.contract).toContain('docs/agent-contract.md') + expect(Object.keys(schema.exitCodes)).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']) + expect(schema.dynamic.hub).toContain('norbix hub --help') + }) + + it('every mutating command has a --dry-run example, every destructive one a --yes example', () => { + const missing: string[] = [] + for (const c of schema.commands) { + if (c.supportsDryRun && !c.examples.some((e) => e.includes('--dry-run'))) missing.push(`${c.id}: --dry-run`) + if (c.destructive && !c.examples.some((e) => e.includes('--yes'))) missing.push(`${c.id}: --yes`) + if (c.examples.length === 0) missing.push(`${c.id}: no example`) + if (c.examples.some((e) => e.includes('...'))) missing.push(`${c.id}: placeholder value`) + } + + expect(missing).toEqual([]) + }) + + it('carries the global flags with their env vars', () => { + const users = schema.commands.find((c) => c.id === 'users delete')! + const project = users.flags.find((f) => f.name === 'project')! + expect(project).toMatchObject({type: 'string', env: 'NORBIX_PROJECT_ID', global: true}) + expect(users.flags.find((f) => f.name === 'yes')).toMatchObject({type: 'boolean'}) + expect(users.args).toEqual([{name: 'id', required: true, description: 'User ID'}]) + }) + + it('describes one command or one topic on request', async () => { + const one = parseSingleJson((await cli(home, ['schema', 'users', 'delete', '--json'])).stdout) as SchemaDoc + expect(one.commands.map((c) => c.id)).toEqual(['users delete']) + + const topic = parseSingleJson((await cli(home, ['schema', 'db', '--json'])).stdout) as SchemaDoc + expect(topic.commands.length).toBeGreaterThan(5) + expect(topic.commands.every((c) => c.id.startsWith('db '))).toBe(true) + + const r = await cli(home, ['schema', 'nothing', '--json']) + expect(r.code).toBe(2) + }) + + it('prints a table without --json', async () => { + const r = await cli(home, ['schema']) + expect(r.code).toBe(0) + expect(r.stdout).toMatch(/users delete\s+Delete a user\s+\[destructive, dry-run\]/) + }) + + it('is stable (snapshot of ids, flags and markers)', () => { + const compact = schema.commands.map((c) => ({ + id: c.id, + destructive: c.destructive, + supportsDryRun: c.supportsDryRun, + args: c.args.map((a) => (a.required ? a.name : `[${a.name}]`)), + flags: c.flags.filter((f) => !f.global).map((f) => `${f.name}:${f.type}`), + })) + expect(compact).toMatchSnapshot() + }) +}) + +describe('norbix hub --json', () => { + it('lists every method with its route and request fields', async () => { + const r = await cli(home, ['hub', 'scheduler', '--json']) + expect(r.code).toBe(0) + const doc = parseSingleJson(r.stdout) as {module: string; methods: Array<{method: string; words: string; destructive: boolean; http: string; path: string; fields: Array<{name: string; kind: string; required: boolean}>}>} + expect(doc.module).toBe('scheduler') + const del = doc.methods.find((m) => m.method === 'deleteSchedulerTask')! + expect(del).toMatchObject({words: 'task delete', destructive: true, http: 'DELETE', path: '/{version}/scheduler/tasks/{Id}'}) + expect(del.fields).toEqual([{name: 'id', type: 'string', kind: 'string', required: true}]) + }) + + it('shows the fields in scope help', async () => { + const r = await cli(home, ['hub', 'scheduler', 'task', 'delete', '--help']) + expect(r.code).toBe(0) + expect(r.stdout).toContain('fields: id (string, required)') + expect(r.stdout).toContain('--field:str') + }) +}) diff --git a/tests/sms-routes.test.ts b/tests/sms-routes.test.ts index fb202b7..bc20880 100644 --- a/tests/sms-routes.test.ts +++ b/tests/sms-routes.test.ts @@ -63,7 +63,7 @@ const routes: Array<[string[], string, string]> = [ [['integrations'], 'GET', `${P}/integrations`], [['integration', ID], 'GET', `${P}/integrations/${ID}`], [['integration', 'enable', ID], 'PUT', `${P}/integrations/${ID}/enable`], - [['integration', 'disable', ID], 'PUT', `${P}/integrations/${ID}/disable`], + [['integration', 'disable', ID, '--yes'], 'PUT', `${P}/integrations/${ID}/disable`], [['integration', 'default', ID], 'PUT', `${P}/integrations/${ID}/default`], [['integration', 'delete', ID, '--yes'], 'DELETE', `${P}/integrations/${ID}`], [['integration', 'test', '--integration', ID, '--to', PHONE], 'POST', `${P}/integrations/test`], diff --git a/tests/sms.test.ts b/tests/sms.test.ts index 1dfd1bf..f8d6e3e 100644 --- a/tests/sms.test.ts +++ b/tests/sms.test.ts @@ -161,7 +161,7 @@ describe('sms integrations', () => { describe('sms integration state changes', () => { const cases = [ {command: SmsIntegrationEnable, method: 'enableSmsIntegration', argv: [ID], output: `Integration ${ID} enabled.`}, - {command: SmsIntegrationDisable, method: 'disableSmsIntegration', argv: [ID], output: `Integration ${ID} disabled.`}, + {command: SmsIntegrationDisable, method: 'disableSmsIntegration', argv: [ID, '--yes'], output: `Integration ${ID} disabled.`}, {command: SmsIntegrationDefault, method: 'setSmsIntegrationAsDefault', argv: [ID], output: `Integration ${ID} is now the default.`}, {command: SmsIntegrationDelete, method: 'deleteSmsIntegration', argv: [ID, '--yes'], output: `Integration ${ID} deleted.`}, ]